page:docs-specs-platform-gaps-phase0-ar-provisioning
WP-RN-03 record: `@fabricstack` scope + Artifact Registry npm provisioning reference
Executed 2026-09-28 on branch phase0/hard-split-registry-guards of a5c-ai/fabric. Spec: docs/specs/platform-gaps/15-private-isolation-rename.md WP-RN-03 (§2 scope decision, §4.1 registry provisioning). Owner approval for the GCP changes was given at the phase0.gcp-provision breakpoint ("Approve (gcloud re-authed) — create fabric-npm + the two IAM bindings, run the WIF smoke publish and the local install"). Developer/CI instructions: docs/user-guide/private-install.md. The public repository a5c-ai/babysitter was never written to (pushedat stayed 2026-09-16T19:59:50Z throughout; only gh api GET calls were issued against it) and nothing was published to registry.npmjs.org.
WP-RN-03 record: `@fabricstack` scope + Artifact Registry npm provisioning
**Scope re-locked to
@fabricstackon 2026-09-28, seescope-relock.md.** This record was produced under the previous private scope@agentfabric; its scope strings were rewritten mechanically at the re-lock (the quoted run logs and transcripts below therefore originally showed the old name, and the smoke package they refer to is the old-scope0.0.0-smoke.2, now a cleanup candidate in AR). The re-run under the new scope is recorded inscope-relock.md. The Artifact Registry repository, IAM policy and WIF principal are scope-agnostic and unchanged.
Executed 2026-09-28 on branch phase0/hard-split-registry-guards of a5c-ai/fabric. Spec: docs/specs/platform-gaps/15-private-isolation-rename.md WP-RN-03 (§2 scope decision, §4.1 registry provisioning). Owner approval for the GCP changes was given at the phase0.gcp-provision breakpoint ("Approve (gcloud re-authed) — create fabric-npm + the two IAM bindings, run the WIF smoke publish and the local install"). Developer/CI instructions: docs/user-guide/private-install.md. The public repository a5c-ai/babysitter was never written to (pushed_at stayed 2026-09-16T19:59:50Z throughout; only gh api GET calls were issued against it) and nothing was published to registry.npmjs.org.
What was created
| Item | Value |
|---|---|
| Scope | @fabricstack (spec 15 §2; final lock at Gate B) |
| GCP project / region | boot-464019 / us-central1 |
| Artifact Registry repository | projects/boot-464019/locations/us-central1/repositories/fabric-npm, format NPM, mode STANDARD_REPOSITORY, description "agentfabric private npm packages (fabric)", created 2026-09-28T15:06:04Z, Google-managed encryption, vulnerability scanning disabled (containerscanning.googleapis.com not enabled) |
| Registry URL | https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/ |
Committed root .npmrc | @fabricstack:registry=https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/ plus a comment line; the pre-existing workspace lines (link-workspace-packages, prefer-workspace-packages, from commit 1f3ea8ad8) were kept; zero credentials |
| Smoke fixture | scripts/ci/fixtures/ar-smoke/package/ (@fabricstack/smoke, publishConfig.registry = registry URL, index.js exports { ok: true }) |
| Smoke workflow | .github/workflows/ar-smoke.yml (id 369233665), workflow_dispatch; a push trigger scoped to its own file only registers it on feature branches, the job is gated on github.event_name == 'workflow_dispatch' |
| Developer/CI doc | docs/user-guide/private-install.md |
Creation detail: gcloud artifacts repositories create fabric-npm --repository-format=npm ... failed twice with INVALID_ARGUMENT: Maven config is not supported for format "NPM" (a gcloud client defect, also with --mode=standard-repository), so the repository was created with the same parameters through the REST API (POST https://artifactregistry.googleapis.com/v1/projects/boot-464019/locations/us-central1/repositories?repositoryId=fabric-npm, body {"format":"NPM","mode":"STANDARD_REPOSITORY","description":"agentfabric private npm packages (fabric)"}, operation d629ee5e-5f29-49bb-8122-db614d5b2ac8). gcloud artifacts repositories describe fabric-npm --location=us-central1 --project=boot-464019 confirms the values in the table.
The spec's second .npmrc line (//us-central1-npm.pkg.dev/boot-464019/fabric-npm/:always-auth=true) was not added: always-auth is not an npm >= 9 option (npm 10 ignores it with a warning), and the brief for this work package asked for the scope-routing line only. Artifact Registry npm requires a bearer token on every request regardless, which the helper provides.
IAM (repository-scoped only; nothing granted at project level)
The CI identity is the existing WIF pair from the a5c-ai organization variables (visibility ALL, so no environment: is needed in workflows): GCP_SERVICE_ACCOUNT=github-deployer@boot-464019.iam.gserviceaccount.com, GCP_WORKLOAD_IDENTITY_PROVIDER=projects/1024539647872/locations/global/workloadIdentityPools/github/providers/a5c-ai, GCP_PROJECT_ID=boot-464019. No new service account or WIF pool was created.
Commands run:
gcloud artifacts repositories add-iam-policy-binding fabric-npm --location=us-central1 --project=boot-464019 \
--member=serviceAccount:github-deployer@boot-464019.iam.gserviceaccount.com --role=roles/artifactregistry.writer
gcloud artifacts repositories add-iam-policy-binding fabric-npm --location=us-central1 --project=boot-464019 \
--member=user:tal@a5c.ai --role=roles/artifactregistry.reader
gcloud artifacts repositories get-iam-policy fabric-npm --location=us-central1 --project=boot-464019 --format=jsonFinal policy, verbatim:
{
"bindings": [
{
"members": [
"user:tal@a5c.ai"
],
"role": "roles/artifactregistry.reader"
},
{
"members": [
"serviceAccount:github-deployer@boot-464019.iam.gserviceaccount.com"
],
"role": "roles/artifactregistry.writer"
}
],
"etag": "BwZcjGj6Uio=",
"version": 1
}Human principal set: user:tal@a5c.ai (reader). Additional developers get roles/artifactregistry.reader on the repository with the first command above; writer stays reserved for the CI service account (spec 15 §4.1: "humans reader-by-default, writer only for release owners").
WIF principal (pre-existing, unchanged)
gcloud iam service-accounts get-iam-policy github-deployer@boot-464019.iam.gserviceaccount.com shows roles/iam.workloadIdentityUser for principalSet://iam.googleapis.com/projects/1024539647872/locations/global/workloadIdentityPools/github/attribute.repository/a5c-ai/fabric (alongside a5c-ai/babysitter, a5c-ai/company, a5c-ai/osb). The pool provider github/providers/a5c-ai has attribute condition assertion.repository_owner == "a5c-ai" and maps attribute.repository. No WIF change was needed or made.
Smoke run (AC1, AC2 publish half)
- Registration: the first
gh workflow run ar-smoke.yml --ref phase0/hard-split-registry-guardsreturnedHTTP 404: workflow ar-smoke.yml not found on the default branch, the same behavior WP-RN-01 hit; the push trigger + job gate fixed it (push run36441528697, conclusionskipped, published nothing). - Dispatch:
gh workflow run ar-smoke.yml -R a5c-ai/fabric --ref phase0/hard-split-registry-guards→ runhttps://github.com/a5c-ai/fabric/actions/runs/36441575117(run number 2, attempt 1, head984e39bde), conclusion **success**, all steps green. - Log excerpts (job
108993127550):
Retrieving credentials from gcloud...
Success!
npm notice PING https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/
npm notice PONG 215ms
npm notice Publishing to https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/ with tag latest and default access (dry-run)
+ @fabricstack/smoke@0.0.0-smoke.2
npm notice Publishing to https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/ with tag latest and default access
+ @fabricstack/smoke@0.0.0-smoke.2
name = '@fabricstack/smoke'
version = '0.0.0-smoke.2'
dist.tarball = 'https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/@fabricstack/smoke/-/@fabricstack/smoke-0.0.0-smoke.2.tgz'- Registry state afterwards:
gcloud artifacts packages list --repository=fabric-npm --location=us-central1→@fabricstack/smoke(2026-09-28T18:11:04); versions →0.0.0-smoke.2only.
Local install transcript (AC2 install half; this machine, following only `docs/user-guide/private-install.md`)
Fresh directory outside the repository (gcloud auth login had already been done for tal@a5c.ai; gcloud auth list → tal@a5c.ai *):
$ printf '@fabricstack:registry=https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/\n' > .npmrc
$ npx -y google-artifactregistry-auth --repo-config=.npmrc --credential-config="$HOME/.npmrc"
Retrieving application default credentials...
Retrieving credentials from gcloud...
Success!
$ npm init -y >/dev/null
$ npm view @fabricstack/smoke version --registry https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/
0.0.0-smoke.2
$ npm install @fabricstack/smoke@0.0.0-smoke.2
added 1 package, and audited 2 packages in 2s
found 0 vulnerabilities
$ node -e "console.log(require('@fabricstack/smoke'))"
{ ok: true }
$ node -e "const p=require('@fabricstack/smoke/package.json');console.log(p.name, p.version, p.publishConfig.registry)"
@fabricstack/smoke 0.0.0-smoke.2 https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/
$ grep resolved package-lock.json
"resolved": "https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/@fabricstack/smoke/-/@fabricstack/smoke-0.0.0-smoke.2.tgz"
$ grep -iE "_authToken|password|token=" .npmrc ; echo $?
1The helper wrote //us-central1-npm.pkg.dev/boot-464019/fabric-npm/:_authToken=... into the user ~/.npmrc and left both project .npmrc files untouched.
AC3: committed `.npmrc` has zero credentials
grep -iE "_authToken|password|token=" .npmrc→ no match (exit 1), before and after the helper and the workflow run.- History secret scan:
git log --all -p -- .npmrc | grep -iE "_authToken|password|token="→ no match across all three historical commits touching the file (836d2677e,d2fd97ff0,1f3ea8ad8). - Other
.npmrcfiles in the tree:packages/observer-dashboard/.npmrc(engine-strict,install-strategyonly). No package sets a conflictingpublishConfig.registry; the 43@a5c-ai/*packages keeppublishConfig.access: publicuntouched (their retarget is WP-RN-04/RN-1). - The workflow's credential step asserts the same grep and
git diff --exit-code -- .npmrcafter the helper runs. - Gate command (run from the repository root after everything else):
npx -y google-artifactregistry-auth --repo-config=.npmrc >/dev/null 2>&1; npm ping --registry https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/ && ! grep -iE "_authToken|password|token=" .npmrc && echo AR-PING-PASS→PONG 332ms,AR-PING-PASS.
Isolation checks
curl -s -o /dev/null -w "%{http_code}" https://registry.npmjs.org/@fabricstack%2fsmoke→404.npm view @fabricstack/smoke --registry https://registry.npmjs.org/from a directory **without** scope routing →E404 '@fabricstack/smoke@*' is not in this registry.- Caveat for anyone repeating that check: run it from inside the repository and npm answers from Artifact Registry, because a scoped
@fabricstack:registryline takes precedence over--registryfor scoped names. That precedence is why the committed.npmrcalone already steers every@fabricstack/*request away from npmjs; it is not a substitute for the G1–G6 guards of WP-RN-04. node scripts/ci/public-push-guard.cjs→scanned 120 files, 0 finding(s), 0 suppressed;--self-test→24/24 assertions passed. The new workflow and fixture add no public-repo reference.gh api repos/a5c-ai/babysitter --jq .pushed_at→2026-09-16T19:59:50Zat start and end;git remote -v→upstream no_push (push)throughout.
Commits
393bf1f03feat(registry): provision Artifact Registry npm + @fabricstack scope routing (WP-RN-03)984e39bdeci(registry): register ar-smoke from feature branches via a gated push trigger- this record: see
git log -- docs/specs/platform-gaps/phase0/ar-provisioning.md
Rollback
# remove the smoke package (keeps the repository)
gcloud artifacts packages delete @fabricstack/smoke --repository=fabric-npm --location=us-central1 --project=boot-464019 --quiet
# remove the IAM bindings
gcloud artifacts repositories remove-iam-policy-binding fabric-npm --location=us-central1 --project=boot-464019 \
--member=user:tal@a5c.ai --role=roles/artifactregistry.reader
gcloud artifacts repositories remove-iam-policy-binding fabric-npm --location=us-central1 --project=boot-464019 \
--member=serviceAccount:github-deployer@boot-464019.iam.gserviceaccount.com --role=roles/artifactregistry.writer
# delete the repository (irreversible; deletes every package in it)
gcloud artifacts repositories delete fabric-npm --location=us-central1 --project=boot-464019 --quiet
# fabric side
git revert 984e39bde 393bf1f03
# developer machines: drop the token line from ~/.npmrc (tokens expire after about an hour anyway)