II.
Page overview
Reference · livepage:docs-specs-platform-gaps-phase0-ar-provisioning
WP-RN-03 record: `@fabricstack` scope + Artifact Registry npm provisioning overview
Inspect the raw attributes, linked wiki pages, and inbound or outbound graph edges for page:docs-specs-platform-gaps-phase0-ar-provisioning.
Attributes
nodeKind
Page
sourcePath
docs/specs/platform-gaps/phase0/ar-provisioning.md
sourceKind
repo-docs
title
WP-RN-03 record: `@fabricstack` scope + Artifact Registry npm provisioning
displayName
WP-RN-03 record: `@fabricstack` scope + Artifact Registry npm provisioning
slug
docs/specs/platform-gaps/phase0/ar-provisioning
articlePath
wiki/docs/specs/platform-gaps/phase0/ar-provisioning.md
article
# WP-RN-03 record: `@fabricstack` scope + Artifact Registry npm provisioning
> **Scope re-locked to `@fabricstack` on 2026-09-28, see `scope-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-scope `0.0.0-smoke.2`, now a cleanup candidate in AR). The re-run under the new scope is recorded in `scope-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:
```bash
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=json
```
Final policy, verbatim:
```json
{
"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-guards` returned `HTTP 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 run `36441528697`, conclusion `skipped`, published nothing).
- Dispatch: `gh workflow run ar-smoke.yml -R a5c-ai/fabric --ref phase0/hard-split-registry-guards` → run `https://github.com/a5c-ai/fabric/actions/runs/36441575117` (run number 2, attempt 1, head `984e39bde`), conclusion **success**, all steps green.
- Log excerpts (job `108993127550`):
```text
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.2` only.
## 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 *`):
```text
$ 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 $?
1
```
The 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 `.npmrc` files in the tree: `packages/observer-dashboard/.npmrc` (`engine-strict`, `install-strategy` only). No package sets a conflicting `publishConfig.registry`; the 43 `@a5c-ai/*` packages keep `publishConfig.access: public` untouched (their retarget is WP-RN-04/RN-1).
- The workflow's credential step asserts the same grep and `git diff --exit-code -- .npmrc` after 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:registry` line takes precedence over `--registry` for scoped names. That precedence is why the committed `.npmrc` alone 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:50Z` at start and end; `git remote -v` → `upstream no_push (push)` throughout.
## Commits
- `393bf1f03` feat(registry): provision Artifact Registry npm + @fabricstack scope routing (WP-RN-03)
- `984e39bde` ci(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
```bash
# 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)
```
documents
[]
Outgoing edges
None.
Incoming edges
contains_page1
- page:docs·PageBabysitter Docs