# Distribution Platform in sx ## Goal Build an App Store-like distribution platform in `sx`, but not limited to iOS apps. It should distribute mobile, desktop, and server/client artifacts through a CI-friendly release workflow. Supported targets for the first product direction: - iOS: IPA metadata, TestFlight links, Enterprise/MDM manifests, artifact-only downloads - Android: APK first, AAB later - macOS: app archives, dmg/pkg metadata, signing and notarization status - Linux: tarballs first, then deb/rpm/AppImage metadata - Windows: zip/installer first, then MSIX/signing metadata The main commands are: - `distd`: the HTTP API, install page, and artifact server - `dist`: the CI/admin CLI The release workflow must be easy to integrate into CI. A CI job should be able to upload artifacts, create a release, attach platform variants, promote a channel, and receive machine-readable JSON output. Product code should live in `sx`. C or system APIs are acceptable only as thin platform backends for capabilities that `sx` cannot express yet. ## Product Shape This is an operational distribution tool first, not a public marketplace. The initial product should feel like a release console: quiet, dense, fast to scan, and optimized for repeated internal/private release work. Core principles: - CI is the primary writer. - Humans inspect, promote, revoke, download, and audit. - Releases are immutable. - Channels are mutable pointers to releases. - Artifacts are content-addressed and never silently replaced. - Every upload, publish, promotion, token change, and deletion writes an audit event. - The first version should be understandable from the filesystem and SQLite database during development. ## Users And Actors - CI runner: publishes releases with `dist ci publish`. - Release manager: promotes channels, checks validation, and reads audit logs. - Developer/tester: downloads the current artifact for a platform/channel. - Admin: creates apps, manages tokens, and configures identity constraints. - `distd`: server/API/artifact store. - `dist`: CLI for CI and local admin work. ## Core Concepts App: - Product being distributed. - Owns identity rules, allowed platforms, releases, channels, and tokens. Release: - Immutable version/build record for one app. - Contains one or more platform artifacts. Artifact: - Uploaded file for a platform. - Stored by digest and linked to release metadata. Channel: - Mutable pointer such as stable, beta, internal, nightly. - Promotion changes the pointer, not the release. Token: - Scoped credential for CI and automation. - Stored hashed, expires or can be revoked. Audit event: - Immutable record of important actions. ## Main Workflows CI publish: 1. CI builds artifacts. 2. CI writes or updates `dist.json`. 3. CI runs `dist ci publish --server "$DIST_SERVER" --token "$DIST_TOKEN" --manifest dist.json --json`. 4. `dist` validates the manifest and streams artifact uploads. 5. `distd` validates metadata and stores artifacts by digest. 6. The release is published. 7. A requested channel is promoted if policy allows it. 8. CI receives JSON with release ids, artifact ids, digests, and URLs. Human release management: 1. Release manager opens the admin UI. 2. They inspect release status, artifact validation, and audit history. 3. They promote, rollback, revoke tokens, or download artifacts. Install/download: 1. User opens an app/channel install page. 2. Platform detection chooses the most relevant artifact or install path. 3. The page shows accurate platform-specific actions and metadata. 4. Access policy determines whether the user can download or install. ## iOS Install Policy iOS needs special handling. The platform must not imply that a normal iPhone can install an arbitrary IPA from a web page. Supported iOS modes: - TestFlight: store a TestFlight link and open Apple's install flow. - Enterprise/MDM: serve a valid HTTPS manifest plist for enrolled devices. - Artifact only: allow authenticated IPA download, without presenting it as a normal mobile install action. This distinction must be visible in both the API model and admin/install UI. ## Architecture Overview The intended stack is: ```text sx language and std primitives -> infra libraries: http, json, hashing, storage, db, auth, archive -> distribution domain: app, artifact, release, channel, token, audit -> interfaces: CLI, HTTP API, install pages, admin UI -> deployment: Docker image, NAS-friendly config, persistent data volume ``` Storage direction: - SQLite first. - Local filesystem artifact storage first. - Object storage adapter later. Deployment direction: - Docker/OCI image. - One persistent `/data` volume for database, artifacts, uploads, config, and logs. - One HTTP port. - `/healthz` endpoint. - Runs behind a reverse proxy for TLS. - Runs as a non-root user. - UGREEN NAS deployment through Docker/Container Manager is a first-version requirement. ## sx Foundation Status Re-evaluated 2026-06-11 against the current sx tree. The original foundation asks have largely landed, with one design difference: sx has no `pub` keyword. Visibility is import-scoped; aliases are the re-export mechanism (`print :: core.print`), and a module's namespace tail carries one level into flat importers. `modules/std.sx` is the curated barrel this plan asked for. Delivered in sx and used by this repo: - Module system: alias imports, alias re-exports, namespace barrels, one-level carry, dir-vs-file ambiguity rejection. - Error handling: the `!` error channel with `raise`/`catch`/`onfail` (bindings take parens), `?T` optionals. - `std` modules under `modules/std/`: core, fmt, list, mem (typed allocator helpers over `alloc_bytes`/`dealloc_bytes`), fs, process, socket (raw TCP), json, xml, cli, hash (streaming SHA-256), log (leveled, no timestamps yet), test (bare assert). Still missing from sx — the forward wishlist; each item either blocks a later subplan or has an explicit local workaround: - Collections: `HashMap` (linear scan over `List` pairs meanwhile). - Strings: validated UTF-8 `String`, `StringBuilder`, explicit Unicode model (byte length, scalar values, grapheme clusters, and display width are distinct; invalid UTF-8 must not silently become a `String`). - Bytes and a full path module (only `path_join`/`basename`/`dirname` today). - `std.fs` directory listing (`src/store/cleanup.sx` shims opendir/readdir/closedir via FFI meanwhile). - Time/clock (publish shims `time(2)` via FFI), random, encodings (base64url, percent). - HTTP server/client and TLS boundary — blocks subplan 04 and remote publish (subplan 03 Slice 3). - SQLite — blocks subplan 02 Slice 2 (`db.json` stands in). - Archive inspection — blocks deep IPA/APK validation (subplan 05). - Config layering (env/file/CLI) and richer testing helpers. ## Implementation Phases Phase 0 - sx language/module prerequisites (done, as-built): - Delivered in sx via alias re-exports and namespace barrels; there is no `pub` keyword. Phase 1 - standard library foundation (partial): - Delivered: fs, process, json, cli, hash, log, test, socket, mem. - Outstanding: HashMap, StringBuilder/Unicode model, time, random, encodings, HTTP/TLS, SQLite, archive (see "sx Foundation Status"). Phase 2 - product domain: - Define apps, releases, artifacts, channels, tokens, policies, and audit events. Phase 3 - storage: - Add SQLite persistence and filesystem artifact storage. Phase 4 - CLI first: - Make `dist ci publish --manifest dist.json --json` work before the admin UI. Phase 5 - HTTP API: - Expose app, release, upload, download, channel, token, and audit endpoints. Phase 6 - artifact validation: - Validate APK and IPA first, then desktop artifact metadata. Phase 7 - channels and install pages: - Add promotion, rollback, download URLs, and platform-specific install pages. Phase 8 - admin UI: - Build a dense operational UI for apps, releases, artifacts, tokens, settings, install pages, and audit logs. Phase 9 - packaging: - Package `distd` and the admin UI into a Docker image suitable for UGREEN NAS. ## CI Contract The primary CI command should be: ```sh dist ci publish \ --server "$DIST_SERVER" \ --token "$DIST_TOKEN" \ --manifest dist.json \ --json ``` The command should: 1. Validate the manifest. 2. Create or find the app. 3. Create a draft release. 4. Upload all artifacts with streaming SHA-256. 5. Validate platform metadata. 6. Publish the release. 7. Promote the requested channel if specified. 8. Print JSON containing release id, artifact ids, digests, and download URLs. ## First Milestone Milestone 1 is complete when: - Required `sx` language/std primitives exist or have explicit temporary boundaries. - `distd` can run locally. - `dist ci publish` can publish a release with at least APK and IPA artifacts. - Artifacts are stored by digest. - SQLite stores apps, releases, artifacts, channels, tokens, and audit events. - A channel can be promoted and rolled back. - Install/download pages accurately handle iOS, Android, and desktop artifacts. - The admin UI can inspect apps, releases, validations, tokens, and audit logs. - A Docker image can run on a UGREEN NAS with a persistent data volume. ## Milestone 1 — Flow Step ↔ Subplan Slice Mapping The flow decomposes subplan 02 (`.agents/subplans/02-domain-and-storage.md`) Slice 1 into two steps. This records that decomposition so each step's scope is verifiable from the repo: - **P2.1 — domain structs + boundary validation.** Delivers subplan-02 *Core Structs* (App, Platform, Release, Artifact, Channel, AuditEvent) and the *boundary validation* portion of Slice 1 — slug, version, channel name, platform id, and required-field presence, each with a distinct typed error. Per the PO ruling this includes `Release.published_at` and `Artifact.metadata`. Code lives under `src/domain/`; the acceptance test is `tests/domain_validate.sx`. - **P2.3 — in-memory repository + persistence.** Delivers the rest of Slice 1: the in-memory repository (create/list/get/update, find-by-slug, find-artifact-by-digest) plus `db.json` persistence. - **Token is in neither P2.1 nor P2.3.** Subplan 02 lists Token under *Core Structs*, but its delivery slice is **Slice 5 — Token Security** (generation, hashing at rest, scopes, expiration/revocation). It is out of the Slice-1 steps above. ## Non-goals For Version 1 - Public marketplace payments. - Reviews and ratings. - Organization billing. - Sophisticated staged rollout targeting. - CDN integration. - Full signing/notarization automation. - Object storage. - Postgres. ## Detailed Execution `PLAN.md` is the overview. Slice breakdowns live in `.agents/subplans/` (01–07). The active milestone slice plan and step progress live in `current/` (`PLAN.md`, `CHECKPOINT-DISTRIBUTION.md`). The multi-agent flow harness that originally executed this plan is retired; work proceeds directly in-session, branch-based, with `make test` green at each step boundary.