ICA PAT Onboarding Guide¶
This guide walks through provisioning an enterprise service-account and issuing a
smk_... Personal Access Token (PAT) for use by ICA (IBM Code Assistant) clients
consuming the SkillMeat SAM contract API.
Prerequisites¶
| Requirement | Check |
|---|---|
Enterprise edition deployed (SKILLMEAT_EDITION=enterprise) |
skillmeat version |
SKILLMEAT_ENTERPRISE_PAT_SECRET set on the target node |
ssh agentic-nuc printenv SKILLMEAT_ENTERPRISE_PAT_SECRET |
Access to SKILLMEAT_ENTERPRISE_SERVICE_ACCOUNTS config |
see §2 below |
alembic current shows latest migration applied |
skillmeat db status |
1. Understand the provisioning model¶
SkillMeat enterprise PAT authentication uses a config-backed service-account registry
(skillmeat/api/auth/service_account_registry.py). There is no REST endpoint for
PAT issuance in this release — provisioning is done by:
- Seeding the service-account user row into
enterprise_usersvia the bootstrap seed script (or the enterprise admin UI once available). - Adding the PAT mapping to
SKILLMEAT_ENTERPRISE_SERVICE_ACCOUNTS— a JSON environment variable read at API startup.
D3 decision (locked): No new PAT issuance endpoint in W4. The
service_account_registryalready resolvesPAT→(user_id, tenant_id); provisioning is seeding and config concern only.
2. Step 1 — Seed the service-account user row¶
The enterprise bootstrap seed (scripts/seed/modules/enterprise_bootstrap.py) creates a
seeder-service-account row (SEEDER_SERVICE_ACCOUNT_UUID =
00000000-0000-0000-0000-000000000001) under the demo tenant automatically.
For a custom ICA service account, run the bootstrap seed against the target database and pass a deterministic UUID for your service account:
# On the target node (or via SSH tunnel)
SKILLMEAT_EDITION=enterprise \
SKILLMEAT_DATABASE_URL=<DATABASE_URL> \
python -m scripts.seed.run \
--profile minimal \
--service-account-uuid <SERVICE_ACCOUNT_UUID> \
--dry-run
Replace <SERVICE_ACCOUNT_UUID> with the UUID you want to assign to the ICA service
account (must be a valid non-zero UUID — see §Gotchas below).
Remove --dry-run to apply.
To verify the row was created:
psql "<DATABASE_URL>" -c \
"SELECT id, clerk_user_id, email, role FROM enterprise_users WHERE id = '<SERVICE_ACCOUNT_UUID>';"
3. Step 2 — Generate an smk_... PAT¶
SkillMeat enterprise PATs are HMAC-signed tokens with the prefix smk_. Generate one
using the PAT utility:
python -c "
import hmac, hashlib, secrets, base64, os
secret = os.environ['SKILLMEAT_ENTERPRISE_PAT_SECRET']
rand = secrets.token_hex(32)
sig = hmac.new(secret.encode(), rand.encode(), hashlib.sha256).hexdigest()[:16]
print(f'smk_{rand[:8]}_{sig}')
"
Important: The first 8 characters after
smk_(rand[:8]above) become the PAT prefix used as the registry lookup key inSKILLMEAT_ENTERPRISE_SERVICE_ACCOUNTS.
Record the output as <PAT> and the first 8 characters after smk_ as <PAT_PREFIX>.
4. Step 3 — Register the PAT mapping in config¶
Add an entry to SKILLMEAT_ENTERPRISE_SERVICE_ACCOUNTS (JSON, new-shape format):
Where:
| Placeholder | Value |
|---|---|
<PAT_PREFIX> |
First 8 chars after smk_ from your generated PAT |
<TENANT_UUID> |
Your enterprise tenant UUID (demo: 00000000-0000-4000-a000-000000000001) |
<SERVICE_ACCOUNT_UUID> |
UUID from Step 1 |
Set this on the target node:
# Append to .env or set in systemd unit Environment=
export SKILLMEAT_ENTERPRISE_SERVICE_ACCOUNTS='{"<PAT_PREFIX>": {"tenant_id": "<TENANT_UUID>", "user_id": "<SERVICE_ACCOUNT_UUID>"}}'
Then restart the API service for the new config to take effect:
5. Step 4 — Initial verification¶
Confirm the PAT resolves correctly before the full SAM round-trip:
# Health check (no auth required)
curl -s http://<NUC_HOST>:8080/health | jq .
# Auth smoke test — should return 200 with user context
curl -s http://<NUC_HOST>:8080/api/v1/user-collections \
-H "Authorization: Bearer <PAT>" | jq .
# Confirm the PAT maps to the right tenant
curl -s http://<NUC_HOST>:8080/api/v1/settings \
-H "Authorization: Bearer <PAT>" | jq '{edition: .edition}'
Where <NUC_HOST> is the enterprise node address (LAN: 10.42.10.76, alias agentic-nuc).
6. SAM contract round-trip exercise (post-merge nuc step)¶
STATUS: Pending post-merge nuc deploy.
The commands below document the reproducible nuc exercise for Phase 5 (P5-T3). They require the nuc to be running ≥
6cffd5621withent_095migration applied. Execute these after the Wave 1 branch is merged and deployed to the nuc.
6.1 Publish a version via SAM¶
# POST /api/v1/sam/artifacts/{artifact_id}/versions
# Publishes a new version of an artifact through the SAM contract.
curl -s -X POST \
http://<NUC_HOST>:8080/api/v1/sam/artifacts/<ARTIFACT_ID>/versions \
-H "Authorization: Bearer <PAT>" \
-H "Content-Type: application/json" \
-d '{
"version": "<VERSION_STRING>",
"content_hash": "<CONTENT_HASH>",
"changelog": "ICA onboarding smoke test"
}' | jq .
# Expected: HTTP 201 with version_id in response
# Record the returned version_id as <VERSION_ID>
6.2 Read the manifest¶
# GET /api/v1/sam/artifacts/{artifact_id}/manifest
curl -s \
http://<NUC_HOST>:8080/api/v1/sam/artifacts/<ARTIFACT_ID>/manifest \
-H "Authorization: Bearer <PAT>" | jq .
# Expected: HTTP 200 with artifact manifest including latest version details
6.3 Submit → approve → promote state machine¶
# Submit for approval (none → pending)
curl -s -X POST \
http://<NUC_HOST>:8080/api/v1/sam/artifacts/<ARTIFACT_ID>/submit \
-H "Authorization: Bearer <PAT>" | jq .
# Approve (pending → approved)
curl -s -X POST \
http://<NUC_HOST>:8080/api/v1/sam/artifacts/<ARTIFACT_ID>/approve \
-H "Authorization: Bearer <PAT>" | jq .
# Gate-check promotion (returns 200 if approved)
curl -s -X POST \
http://<NUC_HOST>:8080/api/v1/sam/artifacts/<ARTIFACT_ID>/promote \
-H "Authorization: Bearer <PAT>" | jq .
Document the concrete artifact IDs, version IDs, and response payloads in
.claude/worknotes/registry-core-w4/e2e-staging-result.md after completion.
D7 Deferral — Team-level PAT scoping¶
D7-DEFERRED (registry-core-w4/P4): Enterprise PAT tokens are issued at tenant scope only for this release. Team-level PAT scoping (assigning a
team_idto a service account's PAT so it inherits team-level access controls) is not supported in MVP staging E2E.When team-scoped service accounts are required, extend the
SKILLMEAT_ENTERPRISE_SERVICE_ACCOUNTSconfig shape to include an optionalteam_idfield, and updateskillmeat/api/auth/enterprise_pat_provider.py(thevalidate()return value) to populateAuthContext.team_idfrom the registry entry.See the
D7-DEFERREDcomment atskillmeat/api/auth/enterprise_pat_provider.pyaround theteam_id=Noneassignment for the exact deferral note.
Gotchas¶
All-zeros tenant UUID footgun¶
Never use 00000000-0000-0000-0000-000000000000 as tenant_id in the
SKILLMEAT_ENTERPRISE_SERVICE_ACCOUNTS config.
The correct demo tenant UUID is 00000000-0000-4000-a000-000000000001 (note the
4000 version segment and a000 variant segment). Using the all-zeros UUID causes
PAT auth to fail silently — the registry will resolve the entry but the tenant context
will be phantom, causing downstream 403s or empty result sets.
The enterprise seed (skillmeat/cache/seeds/enterprise_seed.py) includes a guard
that raises ValueError if DEMO_TENANT_UUID resolves to all-zeros before any DB
writes, which surfaces this misconfiguration at seed time rather than request time.
PAT secret rotation¶
Rotating SKILLMEAT_ENTERPRISE_PAT_SECRET invalidates all existing PATs because
PAT validation is HMAC-based. Reissue all service-account PATs and update
SKILLMEAT_ENTERPRISE_SERVICE_ACCOUNTS with new prefixes after rotation.
Legacy config shape rejected¶
The old {tenant_uuid: user_uuid} config shape is detected at startup and silently
rejected (entries excluded from the PAT index, logged as ERROR). If PAT auth
fails with 401 for a seemingly valid token, check the API startup logs for
LEGACY_ENTERPRISE_SERVICE_ACCOUNTS_SHAPE messages.
API port¶
The SkillMeat API binds on port 8080, not 8000. All curl commands in this
guide use :8080.
Reference¶
| Resource | Path |
|---|---|
| Auth provider | skillmeat/api/auth/enterprise_pat_provider.py |
| Service-account registry | skillmeat/api/auth/service_account_registry.py |
| Bootstrap seed | scripts/seed/modules/enterprise_bootstrap.py |
| Audit RBAC seed (guard) | skillmeat/cache/seeds/enterprise_seed.py |
| SAM versions handler | skillmeat/api/routers/sam_versions_handler.py |
| SAM contract handler | skillmeat/api/routers/sam_contract_handler.py |
| Auth architecture | .claude/context/key-context/auth-architecture.md |
| Authentication setup | docs/user/guides/authentication-setup.md |