# 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).
- 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.
