Uh oh!
There was an error while loading. Please reload this page.
feat(spec)!: refuse inline credentials at publish — driver config + connector authoring door (#7990, spec half) - #8078
Conversation
…onnector authoring door (#7990) Driver family (postgres/mysql/mongo/turso): config.password / config.authToken are declared-unwritable (z.never with the refusal prescription) so tsc, the parse and the connection form's format:'password' secret input all stay wired to the datasource secret binder (sys_secret + external.credentialsRef). Former alias spellings (passwd/pwd/token/jwt/auth_token/authtoken) carry the refusal directly via guidance. Connector: DeclarativeConnectorEntrySchema now refuses non-none authentication on EVERY authored entry — catalog descriptors included (was: provider-bound instances only, ADR-0097 §3). Runtime registerConnector keeps the resolved inline shape. Ledger: 4 retired-keys entries + 2 D3 semantic entries (no D2 conversion — a cleartext credential cannot be mechanically rewritten into an encrypted sys_secret row); regenerated registry, spec-changes, upgrade guide, authorable surface ([RETIRED] flips), api-surface, export-origins, reference docs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0123k4cam2jEAkPmbJeoaY3r
… descriptor migration (#7990) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0123k4cam2jEAkPmbJeoaY3r
…ine-credential-refusal
…egen step 4) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0123k4cam2jEAkPmbJeoaY3r
…ine-credential-refusal
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 7 release-owned page(s) also reference the affected code. These are read-only:
|
…ine-credential-refusal
…egen step 4, second lap) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0123k4cam2jEAkPmbJeoaY3r
Uh oh!
There was an error while loading. Please reload this page.
Part of #7990 — the spec half only of the maintainer-ruled Option A split (comment 5266068845, 「接受你的全部建议。」). The services write/read-path half (including the false "credential-stripped" claim at
datasource-admin-service.ts) and the existing-cleartext-rows migration are deliberately NOT here; the PM files them asBlocked-by:sub-cards at ACCEPT per the claim comment (5266425167). Merging this PR must not close the card.What changes
Driver config family (postgres / mysql / mongo / turso).
config.passwordand turso'sconfig.authTokenbecome declared-unwritable: the key stays in the shape asz.never()carrying the refusal prescription, so the removal is audible intsc(input typenever), in the parse (message names the key, the datasource secret binder →sys_secret, andexternal.credentialsRef— never a bareunrecognized_keys), and in the authorable-surface ratchet ([RETIRED]flip + 4 registered retired-key entries). Former alias spellings (passwd/pwd, tursotoken/jwt/auth_token/authtoken) carry the refusal directly viaguidance— an alias row pointing at a tombstoned key would be the documented two-step-rejection trap.Why tombstone instead of deleting the key: the Studio connection form renders its masked secret input from the
format: 'password'marker in the JSON-Schema projection and routes the value to the top-levelsecret(the binder's door, neverconfig). A hand-tailored never-key keeps{ "not": {}, "format": "password" }in the projection, so the wizard keeps the very input the refusal diverts authors to. Pinned both halves indriver-credential-refusal.test.ts.Connector authoring door.
DeclarativeConnectorEntrySchema(behinddefineStack({ connectors })andPUT /meta/connector/:name) now refuses non-noneauthenticationon every authored entry — catalog descriptors included; previously only provider-bound instances were covered (ADR-0097 §3), which was the ①-d hole. The runtime shape is untouched: plugins handing resolved secrets toregisterConnector(slack/rest/openapi/mcp connectors, engine.ts:1948/2027) keep working, andshared/connector-auth.zod.tsnow documents the runtime-vs-authored split explicitly.Ledger. 4 retired-key entries + 2 D3 semantic entries (structured TODOs). Deliberately no D2 conversion: a cleartext credential cannot be mechanically rewritten into an encrypted
sys_secretrow at load — auto-deleting the key would silently drop a live credential. Regenerated: migration registry, spec-changes.json, upgrade guide, authorable-surface, api-surface, export-origins, reference docs.Changeset bump reasoning
@objectstack/spec: major, following the acceptance-narrowing precedent (#4583datasource-capabilities-retired: authorable-key removal = major + FROM → TO + registered ledger disposition; same route here, with semantic entries instead of a conversion because no lossless mapping exists).@objectstack/example-showcase: patchfor the migrated descriptor. ADR-0087 disposition:registered datasource-config-inline-credential-refused, connector-inline-authentication-publish-refused— gate verified green on this diff.Census (dispatch-mandated): in-repo sites writing an inline credential today
examples/app-showcase/src/system/connectors/index.tsErpCatalogConnector— descriptor with placeholderauthentication: { type: 'api-key', key: 'SET_AT_INSTALL_TIME' }→ migrated (auth scheme moved todescriptionprose; comment documents the instance form withauth.credentialRef).packages/spectest fixtures (postgres/mongo/turso/datasource tests) → triaged individually (re-spelled credential-free, or replaced with refusal pins; the${DB_PASSWORD}placeholder tests re-scoped to non-credential keys with a measured note: nothing resolves${…}placeholders — they reached the client verbatim).packages/qa/downstream-contractDcConnector(oauth2,env:strings) — deliberately NOT migrated: it pinsConnectorSchema, the runtime shape that legitimately carries resolved secrets; migrating it would weaken the runtime-shape pin.external.credentialsRef.Verification (real numbers; consumer sweep direction = DOWNSTREAM consumers of @objectstack/spec, enumerated by name)
check:generatedall 13 current on the merged tree./metadoor 422 envelope pins), runtime 2171, metadata 603, service-automation 940, platform-objects 347, connector-rest/slack/mcp/openapi 16/8/23/32, downstream-contract 14 — all green. Examplesvalidate: crm / showcase / todo all ✓.check:driver-conformance,check:adr-anchors,check:changeset-gate-self-tests,check:cross-package-test-inputs,check:doc-formula-expressions,check:docs-audit-scope,check:i18n,check:merge-driver,check:release-body,check:spec-parsed-alias,check:nul-bytes,check:adr-0087-registration— all PASS locally. Strictness ledger andtest-typecheck-debt.jsonuntouched (no spec: close memory driver's persistence sub-shapes against unknown keys (#4001 batch B) #7985 collision; its memory-driver face is disjoint).sys_metadata.metadatais a general cleartext sink: any authored artefact whose schema permits an inline credential lands it there (datasourceconfig.password, connectorauthentication) #7990 postgres limb went red in BOTH predicted channels — exactly the 6 postgres-facing refusal pins failed (23 others stayed green), and the authorable-surface gate failed from the other side (check (b2): "RETIRED_KEYS_BY_MAJOR entry names a key that is still LIVE — data/PostgresConfig:password"). Restore proven byte-identical bygit hash-object.origin/mainmerged twice (through 22f0daa) via the mechanizedos-regen-merge.shpath; both sides' ledger entries verified present after regeneration.Open questions (recorded, not expanded on — see the report on #7990)
postgresql://user:pass@hostinconfig.url) are a live, unruled door — measured and pinned as a FACT indriver-credential-refusal.test.ts, not refused (PM mechanism note 3).encryptionKeystays writable: credential-shaped, but neither ruled mechanism (binder secret → password/authToken injection;credentialsRef) can carry it — refusing it would leave local-file encryption unconfigurable.externalis refused whenschemaMode: 'managed'(datasource.zod.ts:550), so a code-defined managed datasource now has no spec-level credential slot at all (the wizard path and URL-embedding remain). Belongs to the services sub-card / maintainer.Out-of-scope census finding filed as #8075 (two more schemas permitting inline credentials — third-surface input for the parked Option-B reopen trigger).
Generated by Claude Code