File size: 9,719 Bytes
a6a5d8e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# A11oy UDS Payload

A single-command, signed, declaratively-deployable A11oy payload for
**Defense-Unicorns (UDS)** environments. Drop it into a UDS bundle and run one
`zarf package deploy` — no bespoke installer, no per-environment glue.

The build emits `dist/a11oy-uds/a11oy-uds-<version>.tar.zst` containing:

- Built `@a11oy/core` runtime (orchestration kernel)
- Built `@a11oy/connection` transport layer
- `MANIFEST.json` — per-file `sha256`, size, build timestamp, git SHA
- Either a `cosign` signature (`*.tar.zst.sig`) when `COSIGN_KEY` is set, or
  an unsigned `*.tar.zst.sha256` sidecar otherwise

## Prerequisites

| Tool     | Min version | Required for                            |
| -------- | ----------- | --------------------------------------- |
| `node`   | 18+         | Manifest generation and verification    |
| `tar`    | any         | Fallback packaging when `zarf` missing  |
| `zstd`   | any         | Fallback packaging when `zarf` missing  |
| `zarf`   | 0.36+       | Native Zarf package creation/deploy     |
| `cosign` | 2+          | Signing (only when `COSIGN_KEY` is set) |

The build is **strict by default**: it always runs `tsc` for both packages
and refuses to produce a payload if either build is empty. Setting
`A11OY_UDS_ALLOW_SOURCE_FALLBACK=1` permits dev-only source packaging
(records `sourcePackaged: true` in `MANIFEST.json`) — never use this for
release output.

If `zarf` is unavailable, the build still produces a deterministic
`.tar.zst`, **but writes it to a clearly-separated `dist/a11oy-uds-fallback/`
directory with a `.fallback.tar.zst` suffix.** That fallback is NOT a Zarf
package and cannot be deployed via `zarf package deploy`; it exists so CI
can still validate the manifest/sign path without the `zarf` binary.

If `cosign` is missing (or `COSIGN_KEY` is unset), the build writes an
unsigned `.sha256` sidecar instead of a `.sig`.

## Build

From the repo root:

```bash
pnpm --filter @workspace/a11oy-uds run build
# or, directly:
bash artifacts/a11oy-uds/scripts/build.sh
```

To sign the output:

```bash
export COSIGN_KEY=cosign.key   # path to your cosign private key
bash artifacts/a11oy-uds/scripts/build.sh
```

Output:

- With `zarf`: `dist/a11oy-uds/a11oy-uds-<version>.tar.zst` (+ `.sig` or `.sha256`)
- Without `zarf` (dev only): `dist/a11oy-uds-fallback/a11oy-uds-<version>.fallback.tar.zst`

## Verify

The build runs `scripts/verify-manifest.mjs` automatically and refuses to
produce a tarball if any file's `sha256` does not round-trip. To re-verify on
demand (e.g. after unpacking):

```bash
pnpm --filter @workspace/a11oy-uds run verify
# or against an unpacked tarball:
node artifacts/a11oy-uds/scripts/verify-manifest.mjs /path/to/unpacked
```

If `cosign` was used to sign, verify the signature with the matching public
key:

```bash
cosign verify-blob \
  --key cosign.pub \
  --signature a11oy-uds-<version>.tar.zst.sig \
  a11oy-uds-<version>.tar.zst
```

Otherwise verify the unsigned sidecar:

```bash
cd dist/a11oy-uds && sha256sum -c a11oy-uds-<version>.tar.zst.sha256
```

## Operator runbook

### Deploy

```bash
zarf package deploy a11oy-uds-<version>.tar.zst --confirm
```

This stages the three declared components (`a11oy-core`,
`a11oy-connection`, `a11oy-provenance`) under `/opt/a11oy/` on the target
node.

### Inspect

Before deploy (or any time after), list components, images, and metadata:

```bash
zarf package inspect a11oy-uds-<version>.tar.zst
```

This emits the parsed `zarf.yaml`, the SBOM (if produced by Zarf), and the
per-file sha256 manifest baked into the payload.

### Rollback

```bash
# Remove the deployed package by name (matches metadata.name in zarf.yaml):
zarf package remove a11oy-uds --confirm

# Then re-deploy the previous known-good tarball:
zarf package deploy a11oy-uds-<previous-version>.tar.zst --confirm
```

Because every release ships with a content-addressed `MANIFEST.json` and
either a cosign signature or sha256 sidecar, you can always confirm that the
tarball you're rolling back to is bit-for-bit the one you originally
released.

## Attestation chain (optional component)

The `a11oy-attestations` Zarf component (optional, off by default) ships
a second sidecar — `ATTESTATIONS.json` — alongside `MANIFEST.json`. Where
`MANIFEST.json` is a flat per-file sha256 manifest, `ATTESTATIONS.json`
is a hash-chained provenance record over the *built subjects*
(`a11oy-core`, `a11oy-connection`). It is what the top-level
`szl-mesh` UDS bundle references as
`optionalComponents: [a11oy-attestations]`, and it is what enables
**offline** verification — no registry round-trip, no transparency log.

### Format

```jsonc
{
  "name": "a11oy-attestations",
  "version": "0.1.0",
  "gitSha": "abc1234",
  "builtAt": "2026-05-26T00:00:00Z",
  "hashAlgorithm": "sha256",
  "manifestSha256": "<sha256 of MANIFEST.json bytes>",
  "subjects": ["a11oy-core", "a11oy-connection"],
  "chain": [
    {
      "index": 0,
      "subject": "a11oy-core",
      "fileCount": 42,
      "totalBytes": 123456,
      "subjectSha256": "<digest of canonical subject lines>",
      "prevHash": "0000…0000",          // 64 zeros — genesis
      "entryHash": "<sha256 of link>"
    },
    {
      "index": 1,
      "subject": "a11oy-connection",
      "fileCount": 17,
      "totalBytes": 65432,
      "subjectSha256": "<digest>",
      "prevHash": "<chain[0].entryHash>",
      "entryHash": "<sha256 of link>"
    }
  ],
  "head": "<chain[N-1].entryHash>"
}
```

The subject digest is `sha256` of the canonical line-stream
`"<relPath>\t<sha256>\t<size>\n"` for every `MANIFEST.json` file under
`<subject>/`, sorted by `relPath`. The link hash is
`sha256("<index>\n<subject>\n<subjectSha256>\n<prevHash>\n")`. `prevHash`
for `index = 0` is 64 zeros (genesis). The terminal `head` field is the
last link's `entryHash`, so a verifier only needs to trust `head` to
trust the whole chain.

This is a **hash chain only** — there is no cryptographic signature on
the chain itself. Signing is handled by the existing cosign sidecar at
the tarball level; signing the chain head is intentionally out of scope.

### Verify

The build runs the verifier automatically and refuses to produce a
tarball if any link is broken. To re-verify on demand:

```bash
pnpm --filter @workspace/a11oy-uds run verify:attestations
# or against an unpacked deploy target (MANIFEST.json + ATTESTATIONS.json
# side by side under /opt/a11oy/ once both components are deployed):
node artifacts/a11oy-uds/scripts/verify-attestations.mjs \
  /opt/a11oy /opt/a11oy
```

### Opt in at deploy time

Because the component is `required: false` and `default: false`, a plain
`zarf package deploy` will skip it. Either opt in explicitly:

```bash
zarf package deploy a11oy-uds-<version>.tar.zst \
  --components a11oy-core,a11oy-connection,a11oy-provenance,a11oy-attestations \
  --confirm
```

…or deploy the parent `szl-mesh` UDS bundle, which lists
`a11oy-attestations` under `optionalComponents` for the `a11oy` package.

## Layout

```
artifacts/a11oy-uds/
├── README.md
├── package.json              # @workspace/a11oy-uds (build + verify scripts)
├── zarf.yaml                 # Zarf v1 package definition
├── scripts/
│   ├── build.sh              # End-to-end build + sign/sidecar pipeline
│   ├── write-manifest.mjs    # Generates MANIFEST.json
│   └── verify-manifest.mjs   # Re-hashes every file; fails on mismatch
└── build/                    # (generated) staged payload + MANIFEST.json
```

Build output lives at `dist/a11oy-uds/` at the repo root.

## Registry

The intended release channel is a GitHub Release artifact plus an optional
GHCR/OCI mirror. Treat the GitHub Release assets and their sha256/cosign
sidecars as canonical unless a repository workflow named
`a11oy-uds-publish.yml` is present and green for the exact tag.

| Channel | Coordinates                                          | Signed                  | When                       |
| ------- | ---------------------------------------------------- | ----------------------- | -------------------------- |
| release | GitHub Release assets (`*.tar.zst`, `.sha256`, `.sig`, `.pub`) | yes when `.sig` is attached | tagged UDS release |
| optional mirror | `oci://ghcr.io/szl-holdings/a11oy-uds:<version>` | only when a matching publish workflow is present and green | operator mirror |
| dev | local fallback tarball | unsigned unless `COSIGN_KEY` is set | CI/dev verification only |

Pull a release by name + version:

```bash
zarf package pull oci://ghcr.io/szl-holdings/a11oy-uds:0.1.0
zarf package deploy zarf-package-a11oy-uds-*.tar.zst --confirm
```

Verify the cosign signature (release channel only) against the published
digest:

```bash
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:0.1.0
```

For pre-release testing, use a locally built payload or a GitHub Release asset.
Only use an OCI dev channel if the matching publish workflow exists in this
repository and reports success for the commit under review:

```bash
zarf package pull oci://ghcr.io/szl-holdings/a11oy-uds:dev
```

Release assets should attach the raw `*.tar.zst`, `.sig`, `.pub`, and `.sha256`
sidecars to the corresponding GitHub Release for air-gapped operators who
cannot reach an OCI registry.

## Out of scope

- Publishing to non-OCI registries (S3, Artifactory, etc.)
- Authoring Helm charts beyond what `zarf package create` consumes
- Deploy-time secrets management — UDS operators handle that out-of-band