# a11oy — Security & Provenance ## Threat surface The shipped payload is **pure-functional TypeScript compiled to ESM JavaScript**. It contains: - No network code (no `fetch`, no `http`, no sockets). - No filesystem code (no `fs`, no `child_process`). - No `eval`, no dynamic `Function(...)` construction. - No native bindings, no WASM modules. - No third-party runtime dependencies (the `package.json` files declare zero `dependencies` blocks). The runtime is consumed by a host process that supplies inputs and persists outputs. The host process — not this payload — owns I/O, authentication, and authorization. ## Build provenance Every tarball ships a `MANIFEST.json` at its root with: | field | meaning | |-------------------|-----------------------------------------------------------| | `name` | `a11oy-uds` | | `version` | semver from `artifacts/a11oy-uds/package.json` | | `gitSha` | short SHA of the source commit | | `builtAt` | ISO-8601 UTC build timestamp | | `hashAlgorithm` | always `sha256` | | `sourcePackaged` | `false` on release builds (compiled `dist/`, not `src/`) | | `fileCount` | total file count across both packages | | `totalBytes` | sum of `files[].size` | | `files[].path` | path relative to `/opt/a11oy/` | | `files[].size` | byte length | | `files[].sha256` | SHA-256 hex digest of the file | The manifest is sorted by `path` so a re-run on the same inputs produces byte-identical output. The build script (`artifacts/a11oy-uds/scripts/build.sh`) refuses to package `src/` instead of `dist/` unless the operator explicitly sets `A11OY_UDS_ALLOW_SOURCE_FALLBACK=1` — this is forbidden for release output and the `sourcePackaged` field would surface the violation. ## Signature chain (dev channel — this hand-off) ``` a11oy-uds-.tar.zst ← Zarf package (zstd-compressed tar) a11oy-uds-.tar.zst.sha256 ← GNU coreutils sha256 sidecar a11oy-uds-.tar.zst.sig ← cosign blob signature (PEM-key) a11oy-uds-dev.pub ← matching cosign public key ``` Verify: ```bash sha256sum -c a11oy-uds-.tar.zst.sha256 cosign verify-blob \ --key a11oy-uds-dev.pub \ --signature a11oy-uds-.tar.zst.sig \ a11oy-uds-.tar.zst ``` The public key is also committed to source at `artifacts/a11oy-uds/release-keys/a11oy-uds-dev.pub` so a verifier can pin to the in-source copy rather than the bundled one. Trust root: in-band hand-off + matching public key. Suitable for the dev channel and direct operator delivery. **Not** suitable for ungated download-and-deploy scenarios — use the release channel instead. ## Signature chain (release channel — GHCR + cosign keyless OIDC) Once published from a `v*.*.*` git tag, the same byte-identical tarball is pushed to `ghcr.io/szl-holdings/a11oy-uds:` and signed via cosign keyless OIDC against the GitHub Actions workflow identity. Verify: ```bash # Identity regex matches whichever szl-holdings repo currently hosts the # `a11oy-uds-publish.yml` workflow file. The workflow may live in # `szl-holdings/platform` (monorepo) or `szl-holdings/a11oy` (extracted); # both produce the same byte-identical tarball and a valid Fulcio cert. cosign verify \ --certificate-identity-regexp 'https://github.com/szl-holdings/.+/\.github/workflows/a11oy-uds-publish\.yml@.+' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ ghcr.io/szl-holdings/a11oy-uds: ``` Trust root: GitHub's OIDC issuer + the published workflow identity. Suitable for any UDS / Zarf consumer with internet egress to the Sigstore Rekor + Fulcio infrastructure. ## Determinism The build is reproducible: - Zarf produces a sorted tar with the package metadata canonicalised by the `zarf` binary (no operator-controlled fields leak in). - The non-zarf fallback (development only) uses `tar --sort=name --owner=0 --group=0 --numeric-owner --mtime="${BUILD_TS}"` followed by `zstd -19` so the output sha256 is fixed by the inputs and the build timestamp alone. This is what makes cross-channel digest comparison meaningful: the dev hand-off tarball and the GHCR-published tarball have **the same sha256** when built from the same git SHA at the same `builtAt`. ## Key rotation The dev-channel cosign key is project-managed. To rotate: 1. Generate a new keypair: `COSIGN_PASSWORD="" cosign generate-key-pair` 2. Update `artifacts/a11oy-uds/release-keys/a11oy-uds-dev.pub`. 3. Re-sign the existing tarball(s) and update the `.sig` sidecars. 4. Cut a new prerelease so consumers see the new key alongside the new signature. The release-channel keyless OIDC signature has no static key to rotate — its identity is tied to the GitHub Actions workflow file path and the repo SHA. ## Vulnerability surface Because the runtime has zero third-party runtime dependencies and no I/O, the dependency-derived vulnerability surface is empty. The development dependencies used to **build** the runtime (TypeScript, etc.) do not ship to the operator — they are pruned out of the `dist/` staging step. SBOMs for the package are generated by Zarf at build time and can be extracted post-hoc: ```bash zarf package inspect sbom-extract a11oy-uds-.tar.zst --output ./sbom ``` The extracted SBOM enumerates every staged file with size + sha256 and flags any non-zero dependency entries. For v0.1.0 the dependency entry count is zero.