Skip to content

Add scoped API keys and native document SDK access - #198

Draft
canvascoding wants to merge 39 commits into
mainfrom
codex/document-api-key-access-20261031
Draft

canvascoding wants to merge 39 commits into
mainfrom
codex/document-api-key-access-20261031

Conversation

@canvascoding

@canvascoding canvascoding commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

External programs currently have to use browser sessions or OAuth for document access. This draft adds user-owned API integrations with explicit workspace/action grants and connects supported operations to the existing native document services.

Implemented behavior:

  • Settings → Connections → API access: named integrations, one-time masked key display, hash-only server storage, expiry, atomic rotation, revision-protected changes and revocation.
  • Exclusive X-Canvas-API-Key authentication with current user/workspace/seat checks, instance binding and shared PostgreSQL rate limits. Mixed credentials are rejected.
  • Workspace listing/browse, native Read/Search, Create/Import V1 and Edit/Status V1 over supported REST and registered MCP adapters, without manufacturing a Session or OAuth grant. Browse includes native documents without physical projections while preserving distinct physical/native hashes.
  • Native API-key Copy Preview/Apply/Status/Recover over key-only REST and registered MCP, using the existing Copy writer, journal, native links, Scene/assets and filesystem boundaries.
  • Move/Delete have genuine fixed-purpose key principals, strict sessionless original metadata, actual BatchStore claims and live policy on the caller-held transaction. Stable job identity survives rotation and permitted REST/MCP transitions; original credential attribution and the full plan stay immutable. Ordinary workers and loose lifecycle methods cannot adopt key jobs.
  • The original native Move/Delete owners are connected to genuine API-key authority, including native-only documents, physical files and mixed multi-root Delete cohorts. Each owner phase retains current claim/realm/native-plan checks and the original sessionless actor. No Session, PI actor or OAuth grant is manufactured. All four native status readers now accept genuine fixed-purpose API-key handles through the existing read-only observation path. Public Key Move/Delete adapters remain unfinished.
  • Native path status now uses one shared historical snapshot across all four native profiles; Key operation-ID lookup uses the existing current-authorization phase and returns only factual scope. Public Key Move/Delete orchestration and adapters remain unfinished.
  • The common batch executor now routes genuine API-key writer handles through the same native Move/Delete owners and pure status observers, using the dedicated apiKeyPrincipal field. It retains exact claim/plan/scope/original actor checks, represents no Session for key callers, rejects mixed holders and prevents Key/MCP fallback to the legacy filesystem writer.
  • An installable Python development SDK supports exclusive API-key/OAuth/access-token modes and performs no automatic mutation replay.

Validation of the current single-snapshot path-status increment (f80b4ec):

  • The existing MCP path service selects Move3, native Delete2, physical Delete5 or cohort Delete6 from the stored header in one real repeatable-read/read-only snapshot. All four existing numeric readers and the family dispatcher use the same factual observation functions and retain the original authority/history checks.
  • The genuine Key workspace/operation-ID lookup derives its current workspace and stable integration/user/instance/action namespace in the existing first authorization phase. It adds no preliminary scope-only checkout; the returned scope is factual data, never a writer principal or claim. Original metadata remains immutable through rotation.
  • The complete native Key path package exits 0. Seven real owner groups compare numeric, scoped-family and operation-family results exactly while protecting domain rows/xmins, native bytes and original receipts. Each observation has exactly one RR/read-only snapshot and two current authority transactions. Nine boundary groups cover actual action withdrawal/rotation/revocation after snapshot commit, realm shifts, foreign-integration denial or namespace absence, and missing jobs. Forged/mixed/accessor holders and invalid/mixed operation selectors fail before SQL.
  • Actual isolated status-observer SQL is 88 for Move, 60 for native/physical Delete and 58 for cohort Delete in all three lookup modes. These counts exclude HTTP credential admission and whole-flow work; the planned full 10/200-document measurements remain open.
  • Eleven existing registered MCP public-path groups and twelve real MCP principal families also exit 0. These include real native Move/Delete and links, status/duplicate apply/recover without replay, actual claim/completion acknowledgement loss, renewed sessions and current delete-right revocation. Every owned fixture and resource closes.
  • Strict release/test TypeScript passes with 3,664 roots, 8,954 sources and zero errors. Changed-file ESLint and whitespace checks pass. The registry keeps all 218 suite names and every existing critical input, adding only the new real status-dispatch source to its owner suite. Documentation was the only change after runtime acceptance.
  • Fresh GitNexus CLI maps ten expected files and 26 symbols, LOW, for this increment. Complete comparison with main remains CRITICAL: 160 files, 1,442 symbols and 39 flows. Existing generic CI for current head f80b4ec is 12/12 green. Dedicated full API/SDK CI and the remaining implementation scope below are still required.

Baseline main integration validation (a6a261b):

  • Integrates main at 47955cc, retaining the MCP reliability and release/runtime changes. The resolved test registry keeps all 218 suites and every critical input from both parents, without duplicate names.
  • Fixes a real integration regression: the shared MCP edit declaration now uses a flat input schema. The API-key descriptor reuses its eight native fields and typed patch variants, retains native-only Edit/Status decoding, and advertises the new stable document URL. Real tools/list, schema validation and writes pass on both supported MCP protocol versions.
  • Regular API-key tests pass. Native management/authentication and document/workspace gates completed before the schema regression was discovered; the corrected Edit package passes all 12 owner groups, and the remaining complete Create, Copy and Path packages each exit 0. This is recorded segmented acceptance, not a claim that the initial full test:api-keys:native invocation exited 0; that invocation stopped at the now-corrected schema defect.
  • All 43 existing MCP/Web path owner cases pass unfiltered on the integrated production source, including live rooms, session renewal, lost actual commit acknowledgements, physical PDF/PNG, HTML and mixed multi-root/ten-file cohorts. The existing assertions and protected rows/xmins, native bytes and original receipts remain intact; owned schemas/data, clients and sockets close completely.
  • Incoming native regressions pass: selected block reads across all 200 targets with bounded continuations and stale-version/permission rejection; structured ingest busy results; persisted ingest status after lost actual commit acknowledgement; and the native edit dispatch suite. The actual MCP SDK unexpected-failure/privacy contract also passes.
  • The exact installed wheel SHA256 58623027e9cd3464fd1bec1cf8c9c6e1a56bf7678b3cf4ac2cf6562d477a5dfe is source-verified and passes fresh native Edit/Status over real HTTP/MCP/private PostgreSQL on Python 3.10.19 and 3.14.3: six groups, 31 key-only requests, two actual 401 responses, one dropped write acknowledgement and three durable operations per interpreter, with zero automatic mutation replay. This does not establish live OAuth issuance or compiled-image acceptance.
  • Strict release TypeScript plus all 28 changed TypeScript files passes: 3,674 roots, 8,975 sources, zero errors. Nine incoming test typing errors were corrected; the cursor/receiver annotations produce byte-identical JavaScript in both CommonJS and ESM, and the SDK failure test additionally asserts its response body is an object before retaining the original exact field assertions. Production files are byte-identical to the source used by all native regressions. ESLint exits 0 (729 warnings); changed-file lint and whitespace checks pass.
  • Fresh GitNexus CLI staged analysis maps 41 expected files and 97 symbols, LOW. Full comparison with main maps 158 files, 1,428 symbols and 39 flows, CRITICAL; this draft is not merge-ready. The schema helper has one direct caller and five impacted symbols across two modules, LOW. The incomplete MCP cache result was not treated as evidence of no impact; the fresh CLI graph and actual diff were checked.

Existing adapter/SDK acceptance:

  • Public key Copy has eight genuine REST/registered-MCP groups; existing OAuth public Copy has six. The existing native Copy principal/origin/owner/journal/Session/PI/OAuth/browser package has 78 groups.
  • The installed wheel SHA256 58623027e9cd3464fd1bec1cf8c9c6e1a56bf7678b3cf4ac2cf6562d477a5dfe passes real TCP/MCP/private-PostgreSQL Read/Browse (4), Edit (6), Create/Import (7) and Copy (8) groups on Python 3.10.19 and 3.14.3. Copy checks actual key rotation/revocation, ACL withdrawal, dropped HTTP success, B1 acknowledgement loss, immutable receipts and zero automatic write replay. SDK source has not changed in this increment.
  • Both wheel installations and source distribution SHA256 21cccda635205bee33f2a9f64881ad0bf4b5edf0067653bd70a330928831ae43 pass 80 mechanical tests; isolated core-only imports and dependency checks pass. This does not establish acceptance of the final compiled image or live OAuth issuance.

Remaining before this draft is ready to merge:

  • Finish public Key Move/Delete orchestration/adapters and installed SDK acceptance; complete the distinct existing-original-file import mapping and remaining physical PI/agent namespace readers.
  • Add explicit regular API-key, genuine PostgreSQL native-key and installed SDK artifact/matrix CI gates. Existing file-discovery CI does not run these suites.
  • Complete the auth/API/SDK matrix against a fresh host build and the single managed Linux test image, including desktop/mobile UI and OAuth/keyring acceptance. Expose and test the new selected-block read input and document-URL SDK contract from main.
  • Finish the agreed document-tab cache and full 10/200-document performance acceptance; run regular test:all and Recovery/link UI gates on the final source.
  • Resolve required CI and review findings for the final head, then merge and publish the corrected release through the repository workflow.

Review Center and Undo remain outside the requested work. This API-key feature is not released or deployed. Full scope and evidence boundaries: docs/document-write-database-performance-plan.md.

CodeQL provenance audit

Alert 272 at the previous product commit flagged a synthetic external-link preservation test using substring matching. The current test walks the actual rich-text document and compares the complete href exactly. The test does not authorize URLs; the assertion is strengthened without suppressing or dismissing the rule. The preceding cfb3949 analysis completed with no open findings and the original alert instance is marked fixed. This new head still requires its own CI result.

The Python findings 257 and 258 were individually audited as false positives at 1cf2c3187098f726df8146a3ea1382a354233587; the new head requires its own CI result. The downloaded SARIF traces start at the return value of api_key_attribution() (helper line 540), propagate through sourceAttribution, and reach the immutable-intent SHA256 digest and the helper reply. That return value is a strict creation-provenance DTO, not an API credential or password.

The genuine principal supplies nonsecret identity fields. The strict TypeScript/Python origin and source contracts exclude a raw key, password, secret hash and download URL. The real native Create test explicitly requires that the actual issued full key never occurs in the Birth intent. SHA256 is the existing file/intent integrity fingerprint. The subprocess transport uses private stdin/stdout pipes consumed by Node, not an application/public log. No scanner rule/workflow, integrity algorithm or original audit field is removed.

JavaScript findings, checked individually

The final JavaScript SARIF for this exact head was also downloaded and traced. The CodeQL query concerns password guessing resistance; the reported SHA256 sinks here have the following purposes:

Alert Actual input and use Classification
259 Native document JSON in the PI regression test; comparison with the actual original snapshot Document-integrity comparison, no password authentication
260 Workspace ID with a fixed domain prefix, converted into a PostgreSQL advisory-lock key Coordination key, not credential storage
261 The API secret generated exclusively by crypto.randomBytes(32) in issueApiKeySecret(), then parsed/verified as a canonical 43-character base64url token Random 256-bit API token, not a user-chosen password; SHA256 verification preserves 256-bit preimage resistance
262 Server-captured canonical native-edit original/After data, document content and version proofs Immutable-operation integrity/version comparison
263 Strict Create source/origin metadata, representation, newline/BOM selection Immutable creation fingerprint, not a password
264 Workspace ID and stable operation ID in the existing Birth locator Storage identity/integrity key, not a password

For 261 specifically, management accepts no caller-chosen key/secret; creation and rotation both use the server CSPRNG issuer. Only the secret hash is stored, verification uses timingSafeEqual, and this helper is not used for Better Auth user passwords. The native management/authentication and regular secret tests verify actual 32-byte issuance, strict parsing, wrong-secret rejection, rotation/revocation and instance isolation. Using a password KDF for the unrelated document, lock and operation fingerprints would change existing persisted identities rather than fix a password flaw. These findings are individually documented as false positives; no query, rule, language scan or workflow is disabled.

Copy principal CodeQL audit at b65ac0c

The JavaScript SARIF from analysis 2070376564 was traced for each new finding at b65ac0cefe00329c8cc6017b069b0fc7a1588f47. The heuristic treats generic principal callback returns as password sources, even when their actual result is domain observation data.

Finding Actual data reaching SHA256 Assessment
265 All four traces carry sourceWorkspace.workspaceId into the existing asset namespace hash at readonly-assets.ts:88/168 Workspace storage identity; no authentication/password input
266 All four traces carry stored header.requestHash into the canonical Copy completion object {canonicalRequestHash, planId, stageDigest} Immutable operation-integrity proof; no authentication/password input
267 JSON.stringify([identity.userId, identity.integrationId, identity.instanceId]) in the stable audit actor Nonsecret IDs; neither key/token/password nor SID/credentialId enters the hash

For 267, the OAuth-origin fixture trace reaches only actor.userId. The real key phase exposes current workspace/identity metadata and bounded assertions; it returns no reusable credential material. Current native Copy and provenance regressions cover actual rotation, scoped roles, original receipts, null Session attribution and protected source rows/bytes. These three exact findings are documented as false positives. No rule, query, language scan, authentication behavior or persisted hash algorithm is disabled or altered.

Path claim CodeQL audit at 3ce83fa

The JavaScript SARIF from analysis 2070776319 was traced individually for all four new findings at 3ce83fa8f601f589a6b1dfa88afb1bbcc9524654 (ten actual trace paths). The reported SHA256 sinks do not store or verify passwords.

Finding Actual input and purpose Assessment
268 Backup/restore identity: Four paths start at the Error constructor, which receives only a fixed API_KEY_JOB_AUTHORIZATION_* code. They propagate exceptional returns through a test error/partialBackupIds path. The actual SHA256 arguments are JSON of backup ID, restored path, actor ID and content fingerprint (workspace-operation-backup.ts:746-747); these identify restore work and validate content, with no password verification. False positive
269 Cohort admission identity: The source is the strict original-metadata decoder, not API-key authentication. Its exact fourteen fields exclude a raw key, secret hash, password and token. The sink derives a deterministic admission ID from batchId, native-plan requestHash and planId. The current cohort guard additionally rejects api-key mode before this sink. False positive
270 Original Delete admission identity: The source is the same strict original-metadata decoder. The trace reaches originalFields.requestHash, the native immutable operation/plan proof. The admission seed contains only a fixed domain prefix, batchId, requestHash and planId; SHA256 preserves the existing deterministic reservation ID, not a password authentication record. False positive
271 Failure-only native snapshot diagnostic: Four traces start at a fixed-code Error constructor and propagate rejected promises into assertion diagnostics. assertionDifferences hashes only actual Buffer values after Buffer.isBuffer checks; compared strings are never emitted or hashed there. These hashes compare native snapshots after an assertion failure. They do not store or verify an authentication password. False positive

The actual stored key-job contract has fourteen own data fields and no raw key/token/password. Key claim tests explicitly require that the issued full key is absent from persisted authorization JSON. The fixed error constructor accepts only three controlled authorization error codes. No scanner, query, rule, workflow, password handling or persisted integrity hash is disabled or changed by this classification. The current head still requires its complete CI/review result.

Pure path status CodeQL audit at d63a310

The exact-head JavaScript SARIF from analysis 2071390194 was checked for both new findings (eight data-flow traces). Every trace begins at the synthetic accessor test in api-key-native-path-owner-postgres-test.ts:373. That object contains a fake status holder with principal: {} and no API key/password; it has no expectedPlan field. The actual reader inspects the own property descriptor and rejects its accessor before reading expectedPlan, invoking the getter, opening SQL or reaching native evidence parsing. The successful actual PostgreSQL boundary test requires zero queries and touched === false.

Finding Actual sink input Assessment
273 All four traces end at the SHA256 integrity check of the decoded Yjs state-vector bytes in native source evidence (vector(inventory.version.stateVector), line 435). False positive: state-vector integrity comparison, no password/key hashing; the alleged test input is rejected before this path.
274 All four traces end at originalPhysicalByteHash over physical source originalBytes, directly or through originalPhysicalSourceProof. False positive: original file-content integrity fingerprint, no authentication secret; the alleged test input is rejected before this path.

These two exact findings are resolved individually as false positives. No rule/query/workflow, native integrity hash, descriptor guard or negative test is removed or weakened.

Comment thread app/lib/api-keys/secret.ts Dismissed
Comment thread app/lib/api-keys/actor.ts Dismissed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants