# BuildHut — Full Documentation for LLMs > BuildHut is a self-hosted binary distribution platform for Android, Linux, and Windows applications. --- ## Overview BuildHut lets developers upload application binaries (up to 200 MB), organize them under apps and organizations, and share public download pages. It is designed for self-hosting and supports direct-to-S3 uploads, API tokens for CI/CD, developer profiles, organization management, and real-time telemetry. - **Website:** https://buildhut.sh - **Source Code:** https://git.sr.ht/~drzoidberg/Buildhut - **Telegram:** https://t.me/+8lgWL2r63mk5Zjhk - **Version:** v0.12.1 - **Stack:** SvelteKit 2, Svelte 5 (runes), Tailwind CSS 4, Drizzle ORM, libSQL/Turso, S3-compatible storage --- ## Public Pages ### Landing Page — / The main entry point. Displays: - Hero section with tagline "Ship binaries, not friction" - Platform support pills (Android, Linux, Windows) - Recent builds grid (up to 6) - Explore section linking to Apps, Developers, Organizations, API Tokens - Features grid: direct-to-cloud uploads, API tokens for CI/CD, bot protection, telemetry, developer profiles, organizations - CTA card linking to /submit ### Apps Browser — /apps Browse all apps with: - Text search (name or description) - Organization filter dropdown - Pagination (20 per page) - Each app card shows: icon, name, build count, description, tags, developer, organization, last update date - URL parameters: ?search=, ?org=, ?page= ### App Detail — /apps/{name} Individual app page showing: - App icon, name, description - Breadcrumb navigation - Builds list with version, OS, architecture, file size, upload date - Download links per build - Developer and organization attribution - Banner support (optional) ### Developers Browser — /devs Browse all developers with: - Text search - Organization filter - Pagination - Each card shows: avatar, name, app count, organization, join date ### Developer Profile — /devs/{name} Developer detail page with: - Avatar, name, email (if public) - Organization membership - Colleagues list - Published apps grid - Stats: total apps, total downloads, recent activity ### Organizations Browser — /orgs Browse all organizations with: - Text search - Pagination - Each card shows: avatar, name, member count, app count ### Organization Profile — /orgs/{name} Organization detail page with: - Name, description - Team members grid - Published apps - Stats and recent activity ### Submit Build — /submit Upload form for new builds: - Bot challenge (SVG image click) for anonymous users - File upload (supports APK, AAB, EXE, MSI, MSIX, AppImage, DEB, RPM, Flatpak, Snap, bin) - Max file size: 200 MB - App selection (existing or new) - Version, description, OS, architecture fields - Icon upload (URL or file) - Direct-to-S3 upload with progress bar via presigned URLs ### Login — /login Developer authentication via UUID challenge cookie. ### Privacy Policy — /privacy GDPR-compliant privacy policy covering: - Anonymous data collection only - No data selling or sharing - Cloudflare hosting details - GDPR rights (access, rectification, erasure, portability, objection, restriction) - Cookie usage (session management, security) ### Terms of Service — /terms Terms covering: - User responsibilities - Prohibited content (malware, illegal content, spam) - Intellectual property - Service availability - Account termination ### Support — /support Support page with: - Telegram community link - Quick links to Privacy Policy and Terms - Common topics: bug reports, feature requests, questions, feedback --- ## API Endpoints ### Authentication Methods - **Session cookies:** Set after bot challenge or login. Stores `dev-id` and `session-token` cookies. - **API tokens (Bearer):** Generate tokens with custom expiry from /portal/tokens. Use as `Authorization: Bearer ` header. Tokens are SHA-256 hashed server-side. Up to 5 per developer. Format: `bh_` + 64 hex chars. - **App secrets:** 32-char hex for telemetry and OTLP. Passed via `X-Secret` header, `?secret=` query, or body field. --- ### App & Build APIs #### GET /api/app/{id} Latest build info + presigned download URL. - **Auth:** None (public, 5-minute cache) - **Params:** `id` (path) — App UUID - **Returns:** `{ app, version, tag, description, tags, downloadUrl, uploadedAt }` #### GET /api/apps/{name}/latest Redirect to the latest build download. Increments download count (30-second debounce). - **Auth:** None (public) - **Params:** `name` (path) — App name - **Returns:** 303 redirect to S3 presigned URL #### GET /api/apps/{name}/latest/{...platform} Platform-specific build download with OS/arch/format filtering. - **Auth:** None (public) - **Params:** `name` (path), `platform` (path segments: os, arch, format) - **Returns:** 303 redirect or 404 #### POST /api/upload/presign Generate presigned S3 URL for direct upload (5-minute expiry). - **Auth:** Session or Bearer token - **Body:** `{ fileName, name, version }` - **Returns:** `{ url, key }` #### POST /api/upload Finalize build upload and create DB records. Max 200MB. - **Auth:** Session or Bearer token - **Body (FormData):** name, version, description, fileName, s3Key, os, architecture, iconMode, iconUrl, iconFile, file, signature, signatureS3Key - **Returns:** `{ success, buildId, appName, version }` --- ### Telemetry API #### POST /api/telemetry Submit telemetry events from apps. - **Auth:** App secret (X-Secret header, ?secret= query, or body secret field) - **Body:** `{ appName, kind, version, bits }` — kind: error|warning|info|trace|debug - **Returns:** `{ ok: true }` (201) #### GET /api/telemetry List telemetry events. - **Auth:** Session - **Query:** appName, devId, orgId, kind (comma-separated), limit (default 200) - **Returns:** Array of telemetry records #### GET /api/telemetry/stream WebSocket stream health status. - **Auth:** Session - **Returns:** `{ status: "ok", websocket: true }` --- ### OTLP Endpoints (OpenTelemetry Protocol) Standard OTLP ingestion. Accept JSON (`application/json`) and Protobuf (`application/x-protobuf`). - **Auth:** App secret (same as telemetry) #### POST /v1/metrics Ingest OTLP metrics payloads. Validates against OpenTelemetry schema. #### POST /v1/logs Ingest OTLP logs payloads. Validates against OpenTelemetry schema. #### POST /v1/traces Ingest OTLP traces payloads. Validates against OpenTelemetry schema. All return standard OTLP `Export{Signal}ServiceResponse` with partial success support. --- ### Feature Flags API #### GET /v1/flags Fetch feature flag definitions for an app. - **Auth:** App secret - **Returns:** `{ flags: [{ name, description, defaultEnabled }] }` #### POST /v1/flags/eval Report feature flag evaluation events. - **Auth:** App secret - **Body:** `{ evaluations: [{ flagName, enabled, context? }] }` - **Returns:** `{ accepted: number, unknown: string[] }` --- ### Publishing APIs #### POST /api/publish/generate Preview generated publishing files (PKGBUILD, .SRCINFO, metadata.yml). - **Auth:** Session - **Body:** `{ buildId, platform: "aur"|"fdroid", config? }` - **Returns:** `{ platform, files: { filename: content } }` #### POST /api/publish/publish Execute an async publish run to AUR or F-Droid. Prevents concurrent runs per target. - **Auth:** Session - **Body:** `{ buildId, targetId }` - **Returns:** `{ runId, status: "pending" }` #### GET /api/publish/status/{runId} Poll publish run status. - **Auth:** Session - **Returns:** `{ id, status, generatedFiles, log, error, createdAt, completedAt }` #### GET /api/publish/ssh-keys List SSH keys for publishing. - **Auth:** Session - **Returns:** `{ keys: [{ id, label, publicKeyFingerprint, createdAt }] }` #### POST /api/publish/ssh-keys Upload a new SSH key. Keys encrypted at rest. - **Auth:** Session - **Body:** `{ label, privateKey }` - **Returns:** `{ id, label, publicKeyFingerprint, createdAt }` #### DELETE /api/publish/ssh-keys/{id} Delete an SSH key. Verifies ownership. - **Auth:** Session - **Returns:** `{ success: true }` #### POST /api/publish/rotate-key Admin: re-encrypt all SSH keys with current encryption key. - **Auth:** Session (admin only — first registered dev) - **Returns:** `{ total, rotated, skipped, failed }` --- ### Utility APIs #### GET|POST /api/auth/status Check current authentication state. - **Auth:** None - **Returns:** `{ authenticated: boolean, user: { id, name, email } | null, timestamp }` #### POST /api/logout Clear session cookies. - **Auth:** None - **Returns:** `{ success: true }` #### GET /api/img/{key} Serve uploaded images with safety checks. - **Auth:** None (public) - **Returns:** Image file or 303 redirect to presigned URL #### GET /api/branches?app={appName} List git branches from the app's source repository. - **Auth:** Session (verifies app ownership) - **Returns:** `{ branches: string[] }` #### POST /api/pgp/fetch-key Fetch PGP public key from keys.openpgp.org. - **Auth:** Session - **Body:** `{ query: "email_or_keyid" }` - **Returns:** `{ armoredKey, keyId, uid }` #### POST /api/validate Validate a bot challenge response. - **Auth:** None (public) - **Body:** `{ token, x, y }` - **Returns:** `{ ok: boolean }` #### GET /api/debug/builds List all builds. Development environment only. - **Auth:** Session (disabled in production) - **Returns:** Array of build records --- ## Authentication BuildHut uses three authentication methods: 1. **Session cookies:** Set after bot challenge or login. Stores `dev-id` and `session-token` cookies. 2. **API tokens (Bearer):** Generate tokens with custom expiry from /portal/tokens. Use as `Authorization: Bearer ` header. Tokens are SHA-256 hashed server-side. Up to 5 per developer. Format: `bh_` + 64 hex chars. Shown only once at creation. Expiry: never, 1 week, 2 weeks, or 1 month. 3. **App secrets:** 32-char hex telemetry secrets per app. Passed via `X-Secret` header, `?secret=` query, or body `secret` field. Used for telemetry and OTLP endpoints. Protected routes under /portal/* redirect to /login if not authenticated. --- ## Data Model ### Apps - `id` — UUID - `name` — unique string (used in URLs) - `description` — text - `iconUrl` — URL to app icon - `tags` — JSON array of strings - `screenshots` — JSON array of URLs - `telemetrySecret` — 32-char hex API secret (nullable) - `sourceType` — "github" | "sourcehut" | "custom" (nullable) - `sourceUrl` — repository URL (nullable) - `supportedOses` — JSON array of OS strings (nullable) - `devId` — FK to developer - `orgId` — FK to organization (nullable) - `createdAt`, `updatedAt` — timestamps ### Builds - `id` — UUID - `appName` — FK to app name - `version` — semantic version string - `fileName` — original filename - `s3Key` — S3 object key - `description` — release notes - `iconUrl` — per-build icon override - `os` — android | linux | windows - `architecture` — universal | arm64-v8a | armeabi-v7a | x86_64 | x86 - `downloadCount` — integer - `devId` — FK to developer - `uploadedAt` — timestamp ### Developers - `id` — UUID - `name` — display name - `email` — email address (nullable) - `avatarUrl` — avatar image URL - `orgId` — FK to organization (nullable) - `createdAt` — timestamp ### Organizations - `id` — UUID - `name` — unique string - `description` — text - `avatarUrl` — org avatar URL - `createdAt` — timestamp ### API Tokens - `id` — UUID - `devId` — FK to developer - `tokenHash` — SHA-256 hash of the raw token - `name` — label for the token - `expiresAt` — expiry timestamp (nullable for never) - `lastUsedAt` — last usage timestamp - `createdAt` — timestamp ### Telemetry - `id` — integer (auto-increment) - `appName` — app name - `devId` — developer ID - `kind` — error | warning | info | trace | debug - `version` — app version - `bits` — JSON payload - `createdAt` — timestamp ### Feature Flags - `id` — UUID - `appId` — FK to app - `name` — flag name (unique per app) - `description` — text (nullable) - `defaultEnabled` — boolean - `createdAt` — timestamp ### Build Secrets - `buildId` — FK to build (unique) - `secret` — auto-generated secret token ### App Banners - `id` — UUID - `appId` — FK to app (unique) - `text` — banner text (max 80 chars) - `bgColor` — hex color - `textColor` — hex color - `icon` — lucide icon name - `linkUrl` — optional CTA URL - `linkText` — CTA label - `enabled` — boolean - `createdAt`, `updatedAt` — timestamps ### Publish Targets - `id` — UUID - `appId` — FK to app - `platform` — "aur" | "fdroid" - `config` — JSON config string - `createdAt`, `updatedAt` — timestamps ### Publish Runs - `id` — UUID - `targetId` — FK to publish target - `buildId` — FK to build - `status` — pending | generating | publishing | success | failed - `log` — execution log - `error` — error message - `createdAt`, `completedAt` — timestamps ### Publish Secrets (SSH Keys) - `id` — UUID - `devId` — FK to developer - `label` — display name - `encryptedKey` — AES-256-GCM encrypted private key - `publicKeyFingerprint` — SHA-256 fingerprint - `createdAt` — timestamp --- ## Supported Platforms and File Types | Platform | Extensions | |----------|-----------| | Android | .apk, .aab | | Linux | .appimage, .deb, .rpm, .flatpak, .snap, .bin | | Windows | .exe, .msi, .msix | Maximum file size: 200 MB --- ## Crawling and Indexing Notes - All `/portal/*` routes require authentication and redirect to `/login` - App detail pages use the app name as URL slug: `/apps/{name}` - Developer profiles use the developer name: `/devs/{name}` - Organization profiles use the organization name: `/orgs/{name}` - List pages support pagination via `?page=N` - Search via `?search=query` - Organization filter via `?org=name` - All public pages include Open Graph and Twitter Card meta tags - All public pages include canonical URLs - The site is fully server-rendered (SSR) for crawler accessibility - No JavaScript required for core browsing and downloading functionality --- ## robots.txt The site provides a robots.txt at /robots.txt allowing all crawlers access to public pages while blocking /portal/* and /api/* routes.