Skip to content

Commit 93b8dfd

Browse files
committed
fix: complete Registry publish and install flow
1 parent fd1b840 commit 93b8dfd

20 files changed

Lines changed: 576 additions & 72 deletions

‎CHANGELOG.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@
2222
`CELLSCRIPT_REGISTRY_URL` Git/offline override, and the website renders the
2323
live Registry with a clearly labelled read-only bundled mirror only when the
2424
API is unavailable. The former Registry Coming Soon surface is removed.
25+
First-publish admission is now user-reachable end to end: `cellc auth
26+
namespace claim` and the submit page's **Claim namespace** action explicitly
27+
establish namespace ownership between capability registration and publish.
28+
Publish admission now commits package, snapshot, version, capability-use,
29+
acceptance-audit, and completed-idempotency state in one database transaction;
30+
pre-admission failures release the request-owned nonce and retry reservation,
31+
while production readiness verifies both managed object-store prefixes and
32+
volume initialization repairs their ownership and modes recursively.
33+
Explicit unverified/quarantined install acknowledgements are persisted in
34+
dependency tables, preventing lock refreshes and later builds from losing the
35+
caller's risk policy.
2536
- Close the 0.23 syntax-audit consistency gaps: canonical type declarations
2637
now use comma-terminated fields, syntax-combination gates cover canonical and
2738
comma-free compatibility input, checked example mirrors use named `U64_MAX`

‎crates/cellscript-tools/src/tooling_release.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ pub fn run(root: &Path) -> Result<()> {
471471
"registry package '{}/{}@{}' has no source_hash in registry.json",
472472
"public registry package '{}/{}@{}' has no immutable source snapshot",
473473
"source_hash mismatch for '{}/{}@{}': expected '{}', got '{}'",
474+
"allow_unverified: detailed.allow_unverified",
474475
"Git { url: String, revision: String }",
475476
"pub fn consistency_issues(&self, manifest: &PackageManifest) -> Vec<String>",
476477
"pub fn replace_with_resolved(&mut self, resolved: &HashMap<String, ResolvedPackage>)",
@@ -482,6 +483,7 @@ pub fn run(root: &Path) -> Result<()> {
482483
&[
483484
"cellc_rejects_registry_dependency_without_namespace",
484485
"cellc_build_resolves_registry_dependency_and_writes_phase1_lockfile",
486+
"cellc_auth_namespace_claim_posts_signed_capability_payload_to_registry_api",
485487
"cellc_install_path_updates_lockfile_and_remove_prunes_it",
486488
"cellc_fmt_subcommand_formats_sources",
487489
"cellc_run_subcommand_executes_pure_elf_package",
@@ -494,6 +496,7 @@ pub fn run(root: &Path) -> Result<()> {
494496
"tests/registry.rs",
495497
&[
496498
"package_manager_resolves_registry_dependency_with_source_hash_from_local_git_fixture",
499+
"package_manager_persists_unverified_registry_policy_in_dependency_manifest",
497500
"package_manager_rejects_registry_source_hash_mismatch",
498501
"lockfile_consistency_accepts_matching_registry_source",
499502
],

‎docs/CELLSCRIPT_GATE_POLICY.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ The `ci` gate also typechecks, tests, and performs a Wrangler dry-run build of
6060
database/static-object shape to the compiler-generated registry entry. It is
6161
local service coverage, not evidence
6262
that Cloudflare, R2, Hyperdrive, Neon, DNS, or a production deployment works.
63+
The CLI coverage includes the explicit first-publish admission sequence:
64+
`cellc auth capability submit`, `cellc auth namespace claim`, then
65+
`cellc publish`. Capability registration does not silently claim a namespace;
66+
the claim response must be `active` before the write API accepts a version.
67+
Explicit `--allow-unverified` and `--allow-quarantined` install choices are
68+
persisted per dependency so the lock refresh and later builds exercise the
69+
same auditable resolver policy.
6370

6471
The full gate reads `scripts/ckb_acceptance_pin.json` and rejects a CKB checkout
6572
whose revision or worktree differs from the pin. Its report binds the CKB

‎docs/CELLSCRIPT_REGISTRY_PHASE1.md‎

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,9 @@ cellc auth capability create --principal-id <principal_id> --scope publish:names
311311
cellc auth capability submit --payload capability-payload.json --joyid-signature joyid-signature.json
312312
-> signed payload is submitted to the registry write API
313313
-> registry records the key's scope, expiry, principal_type, and principal_id
314+
cellc auth namespace claim --namespace namespace --payload capability-payload.json --joyid-signature joyid-signature.json
315+
-> the same JoyID authorisation proves ownership of the namespace named by the publish scope
316+
-> reserved namespaces remain pending until administrator review activates them
314317
315318
cellc publish
316319
-> CLI signs the publish payload with the local publisher credential
@@ -386,9 +389,9 @@ registry service:
386389
`--idempotency-key` or `CELLSCRIPT_REGISTRY_IDEMPOTENCY_KEY` when retrying the
387390
same signed publish request;
388391
- if publish admission fails after reserving the retry key but before accepting
389-
the package version, the registry releases that `processing` reservation; the
390-
consumed signed nonce still cannot be reused, so retry with a fresh publish
391-
payload/signature and the same CI retry key;
392+
the package version, the registry releases that `processing` reservation and
393+
only the nonce row created by the failed request, so the exact signed request
394+
can be retried; admission metadata then commits in one database transaction;
392395
- build verification, artifact checks, deployment checks, chain RPC reads, and
393396
search indexing run asynchronously in bounded queues;
394397
- rate limits apply per IP, ASN, JoyID principal, credential, namespace, and
@@ -458,14 +461,17 @@ mirrored `registry.json` and tag path.
458461
```bash
459462
cellc auth capability create --principal-id <principal_id> --scope publish:cellscript/amm_pool --expires 90d --json > capability-payload.json
460463
cellc auth capability submit --payload capability-payload.json --joyid-signature joyid-signature.json
464+
cellc auth namespace claim --namespace cellscript --payload capability-payload.json --joyid-signature joyid-signature.json
461465
cellc publish
462466
```
463467

464468
If `--capability-pubkey` is omitted, `cellc auth capability create` generates a
465469
local P-256 capability key and stores the private key in the OS keychain. The
466470
printed payload is the exact JoyID challenge to sign and submit to the registry
467-
write API through `cellc auth capability submit`. Once the capability is
468-
registered, `cellc publish` computes a source hash from the current source tree,
471+
write API through `cellc auth capability submit`. The namespace must then be
472+
claimed with `cellc auth namespace claim`; a reserved claim may need operator
473+
review before it becomes active. Once the capability and namespace ownership
474+
are active, `cellc publish` computes a source hash from the current source tree,
469475
reads build artifacts for their hashes, signs the concrete publish payload with
470476
the capability key, uploads an immutable source snapshot, and submits the
471477
version entry to the registry. A successful publish returns the canonical
@@ -602,7 +608,9 @@ source-hash mismatch rejection.
602608
**Production snapshot resolution**: public status authority, required snapshot
603609
descriptors, bounded no-redirect download, object SHA-256, safe unique paths,
604610
per-file BLAKE2b, package-coordinate checks, whole-tree source hash, and atomic
605-
cache materialisation.
611+
cache materialisation. Explicit `--allow-unverified` / `--allow-quarantined`
612+
installs persist the chosen risk policy in the dependency table so later lock
613+
refreshes and builds enforce the same auditable choice.
606614

607615
**Package/build identity**: namespace initialization, build lockfile identity,
608616
package verification, artifact/metadata/schema/ABI/constraints hash recording,

‎docs/CELLSCRIPT_REGISTRY_PRODUCTION_BOUNDARY_ADR.md‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,9 @@ The CLI sends an `Idempotency-Key` on publish; it can derive the key from the
158158
exact request or accept `--idempotency-key` /
159159
`CELLSCRIPT_REGISTRY_IDEMPOTENCY_KEY`. If admission fails after reserving the
160160
key but before the package version is accepted, the registry releases that
161-
`processing` reservation. Because the signed publish nonce may already be
162-
consumed, retrying with the same CI retry key requires a new publish payload and
163-
capability signature.
161+
`processing` reservation and the nonce row owned by that failed request, so the
162+
same signed request can be retried. Package, snapshot, version, capability-use,
163+
acceptance-audit, and completed-idempotency records are committed atomically.
164164

165165
## CI Publishing
166166

@@ -215,13 +215,13 @@ Current production abuse controls:
215215
- principal-scoped quota and namespace-claim cooldown are counted only after
216216
the JoyID signature has been verified, so forged payloads cannot burn another
217217
publisher's principal quota;
218-
- signed publish nonces are consumed before object storage writes, so replayed
219-
publish payloads fail before expensive work;
218+
- signed publish nonces are reserved before object storage writes, so replayed
219+
publish payloads fail before expensive work; a failed pre-admission request
220+
releases only its own nonce row so the exact request remains safely retryable;
220221
- `Idempotency-Key` is supported for publish retries: the same logical request
221222
replays the stored response, while the same key with different payload content
222223
is rejected; failed pre-admission writes release a matching `processing`
223-
reservation so CI can retry with the same retry key and a newly signed publish
224-
payload;
224+
reservation and request-owned nonce so CI can retry the same signed payload;
225225
- request body, metadata field, source snapshot, and artifact sizes are capped;
226226
- duplicate source/manifest hashes are deduplicated or throttled;
227227
- existing package versions are rejected before source snapshot writes;
@@ -373,6 +373,9 @@ Default resolver policy:
373373
`--allow-unverified`;
374374
- quarantined entries require a stronger explicit flag such as
375375
`--allow-quarantined`;
376+
- `cellc install` persists either acknowledgement on that dependency's
377+
`Cell.toml` table, so lock refreshes and later builds preserve the explicit
378+
choice instead of silently dropping it;
376379
- default search, recommendations, and production-visible package lists show
377380
only entries that passed the required baseline checks;
378381
- exact pins keep reproducibility, but warning and explicit-allow policy must

‎docs/releases/CELLSCRIPT_0_23_RELEASE_NOTES.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ the Off-Chain Session Runtime profile remain roadmap work.
2727
| Build identity | The resolved profile independently combines edition, target, primitive assurance, metadata schemas, and entry/witness ABIs, then binds them into metadata, registry, lock, deployment, receipt, and builder records. |
2828
| Registry contract | The deployed publish contract requires Edition 2026 plus its compatibility-profile hash from CLI signature through API, Postgres, version-addressed JSON, and website; assurance states require ordered evidence. |
2929
| Registry operations | `api.registry.cellscript.dev` and `registry.cellscript.dev` run as an isolated self-hosted Postgres/Node/object-volume/read-only-nginx stack behind trusted TLS. |
30+
| Registry retry safety | Pre-admission failures release only the failed request's nonce and retry reservation; accepted metadata commits transactionally, and readiness covers the actual managed object prefixes. |
31+
| Registry install policy | Explicit unverified/quarantined install acknowledgements persist per dependency, so lock refresh and subsequent builds retain the same auditable risk choice. |
3032
| Tooling | CLI, LSP, WASM, website bindings, examples, and package tooling use the same edition contract. |
3133
| Syntax audit | Canonical type fields use trailing commas, checked examples use named `u64` boundaries, and compatibility plus CKB-VM regressions cover both source and witness placement. |
3234
| Native gate | Active test, fixture, evidence, and release tooling is Rust, shell, or Node; repository policy rejects Python source reintroduction. |
@@ -151,6 +153,10 @@ states.
151153
compatibility-profile hash, and use the checked-in fixture only as an
152154
explicitly labelled read-only mirror during API failure. The Coming Soon
153155
surface is removed.
156+
- `cellc auth namespace claim` and the submit page's **Claim namespace** action
157+
expose the namespace-ownership admission step required before a package's
158+
first public publish. Capability registration no longer appears to imply a
159+
claim that the write API never created.
154160
- Production operations include dependency-aware readiness, bounded proxy and
155161
application request bodies, persistent Postgres/object volumes, and a daily
156162
systemd backup. The first backup passed SHA-256 checks plus non-destructive
@@ -260,6 +266,14 @@ curl --fail --silent --show-error https://registry.cellscript.dev/health
260266
curl --fail --silent --show-error https://cellscript.dev/registry/ > /dev/null
261267
```
262268

269+
On 2026-07-31, a disposable cryptographically valid WebAuthn-shaped P-256
270+
fixture completed capability registration, namespace claim, signed publish,
271+
same-request idempotent replay, static snapshot reads, a fresh-directory
272+
install/check/build, capability revocation, and rejection of a later publish.
273+
Its exact database and live object records were removed after the test; the six
274+
object files remain in the server's isolated recovery directory rather than the
275+
served object volume.
276+
263277
These endpoints prove the deployed service boundary, not a publisher-owned
264278
JoyID signature or first-package install. That interactive positive flow
265279
remains the explicit adoption checkpoint.

‎docs/wiki/Tutorial-12-Phase1-Registry-End-to-End.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,14 @@ flow, then publish:
7171
```bash
7272
cellc auth capability create --principal-id <principal_id> --scope publish:cellscript/amm_pool --expires 90d --json > capability-payload.json
7373
cellc auth capability submit --payload capability-payload.json --joyid-signature joyid-signature.json
74+
cellc auth namespace claim --namespace cellscript --payload capability-payload.json --joyid-signature joyid-signature.json
7475
cellc publish --json
7576
```
7677

78+
Namespace ownership is an explicit admission step, not a side effect of
79+
capability registration. The claim must be `active` before the first publish;
80+
reserved namespaces may return a pending review status.
81+
7782
The public write API admits package metadata, but consumers still verify the
7883
source and build identity locally.
7984

@@ -128,6 +133,11 @@ source snapshot. It verifies the snapshot descriptor's SHA-256, rejects opaque
128133
or path-escaping content, verifies every file's BLAKE2b digest, reconstructs the
129134
source tree atomically, and checks `Cell.toml`, source hash, Edition 2026, and
130135
compatibility-profile identity.
136+
For a direct `source_published` or `indexed_pending` install, pass
137+
`--allow-unverified`; incident review of a quarantined entry additionally needs
138+
`--allow-quarantined`. `cellc install` persists these acknowledgements on that
139+
dependency's `Cell.toml` table, so lock refreshes and later builds retain the
140+
same explicit policy.
131141
`CELLSCRIPT_REGISTRY_URL` is an explicit Git/offline override, not an automatic
132142
fallback from a failed production lookup. Registry packages otherwise use the
133143
same fail-closed principle as path and Git dependencies: the selected source

‎roadmap/CELLSCRIPT_0_23_ROADMAP.md‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ alternative deployment, not a claim about the current topology.
197197
mirror used only when the API is unavailable.
198198
- [x] Keep the CCC/JoyID submit page on the same canonical
199199
`cellscript-registry-auth-v1` capability protocol.
200+
- [x] Expose namespace ownership as an explicit first-publish step through
201+
`cellc auth namespace claim` and the submit page, matching the deployed
202+
`/v1/namespaces/claim` admission boundary.
200203
- [x] Implement and expose public search/detail/evidence reads plus ordered
201204
evidence promotions.
202205
- [ ] Complete a publisher-owned JoyID capability, namespace claim, publication,
@@ -212,8 +215,8 @@ alternative deployment, not a claim about the current topology.
212215
key persistence in the OS keychain, and CI signing via
213216
`CELLSCRIPT_CAPABILITY_PRIVATE_KEY_PKCS8_B64`.
214217
- Confirm idempotency (`Idempotency-Key`, `x-idempotency-status: replayed`),
215-
nonce consumption ordering, and the fail-fast-before-object-storage rule
216-
against the live write service.
218+
request-owned nonce release on pre-admission failure, transactional admission,
219+
and the fail-fast-before-object-storage rule against the live write service.
217220
- `cellc install`/`cellc update` now query
218221
`api.registry.cellscript.dev` by default, select only accepted public
219222
statuses, then download the version's immutable Registry snapshot and verify
@@ -239,6 +242,11 @@ Production-readiness evidence currently proves:
239242
- the daily systemd backup produces checksum-verified Postgres and object-store
240243
archives, and both archive formats pass non-destructive restore inspection;
241244
- the website serves the live Registry and contains no Coming Soon surface.
245+
- a cryptographically valid WebAuthn-shaped P-256 fixture completes capability
246+
registration, explicit namespace claim, signed publish, idempotent replay,
247+
API/static/snapshot reads, and a fresh-directory install/check/build against
248+
production; this proves deployment mechanics but is not publisher-owned
249+
JoyID evidence.
242250

243251
The remaining release checkpoint is intentionally narrower but real: complete
244252
the positive publisher-owned JoyID flow and install its first accepted source

‎services/registry-api/README.md‎

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,12 @@ Cloudflare bindings/secrets.
240240
it is safe; already-applied migration files are skipped.
241241

242242
`GET /health` is a process liveness check. `GET /ready` performs live store and
243-
object-adapter checks and returns `503` until every required dependency and the
244-
admin token are ready. `NAMESPACE_CLAIM_COOLDOWN_SECONDS` defaults to `3600`;
245-
lower it only for controlled staging tests.
243+
object-adapter checks, including write access to both managed
244+
`source-snapshots` and `packages` prefixes, and returns `503` until every
245+
required dependency and the admin token are ready. The production volume
246+
initializer repairs ownership and directory/file modes recursively before the
247+
API starts. `NAMESPACE_CLAIM_COOLDOWN_SECONDS` defaults to `3600`; lower it
248+
only for controlled staging tests.
246249

247250
## Admin Governance Boundary
248251

@@ -288,11 +291,16 @@ is submitted to the write API:
288291
cellc auth capability create --principal-id <principal_id> --scope publish:ns/pkg --expires 90d --json > capability-payload.json
289292
# Sign capability-payload.json with the production JoyID path exposed through CCC.
290293
cellc auth capability submit --payload capability-payload.json --joyid-signature joyid-signature.json
294+
cellc auth namespace claim --namespace ns --payload capability-payload.json --joyid-signature joyid-signature.json
291295
```
292296

293297
The registry submit page can sign the same payload through the CCC JoyID CKB
294298
signer and submit it directly to `/v1/capabilities`. The signed response can
295299
also be copied as `joyid-signature.json` for the CLI submit path.
300+
The separate **Claim namespace** action, or `cellc auth namespace claim`, sends
301+
the same signed authorisation to `/v1/namespaces/claim`. A first publish is
302+
intentionally rejected until that claim is active; reserved namespaces may
303+
remain pending for administrator review.
296304

297305
The submit page derives the preferred `principal_id` from the connected JoyID
298306
signer and exposes a copy action. The API verifies that the JoyID signature's
@@ -351,9 +359,11 @@ request. It can be pinned with `--idempotency-key` or
351359
same request.
352360

353361
If publish admission fails before the package version is accepted, the write API
354-
releases the matching `processing` idempotency reservation. The signed publish
355-
nonce may already have been consumed, so a later retry with the same CI retry key
356-
must use a freshly generated publish payload and capability signature.
362+
releases both the matching `processing` idempotency reservation and the nonce
363+
record created by that request. The exact signed request can therefore be
364+
retried safely. Package, snapshot, version, capability-use, acceptance-audit,
365+
and completed-idempotency records commit in one database transaction; immutable
366+
object writes happen before that transaction and may be repeated safely.
357367

358368
Successful publish returns a direct static read URL shaped as:
359369

‎services/registry-api/deploy/docker-compose.production.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ services:
2828

2929
object-store-init:
3030
image: alpine:3.22
31-
command: ["sh", "-c", "chown -R 1000:101 /objects && chmod 2750 /objects"]
31+
command: ["sh", "-c", "chown -R 1000:101 /objects && find /objects -type d -exec chmod 2750 '{}' ';' && find /objects -type f -exec chmod 0640 '{}' ';'"]
3232
volumes:
3333
- registry-objects:/objects
3434
restart: "no"

0 commit comments

Comments
 (0)