Skip to content

feat(modelkey): ctxm_ model-key format — Base62 body, CRC32 checksum, hash and hint (W01-2) - #82

Merged
GottZ merged 2 commits into
rootfrom
feat/oai-w01-2
Nov 3, 2026
Merged

GottZ merged 2 commits into
rootfrom
feat/oai-w01-2

Conversation

@GottZ

@GottZ GottZ commented Nov 3, 2026 •

Copy link
Copy Markdown
Owner

Summary

W01-2 (RC-B, first wave of the credential class): new stdlib-only leaf package internal/modelkey — the ctxm_ model-key format external consumers will present to the OpenAI-compatible /v1 surface. No caller yet; the store (W01-3), the knowledge-path refusal (W01-4) and ModelAuth (W01-6) land in later waves.

  • 54 bytes: ctxm_ + 43 Base62 body (256 bits from crypto/rand, rejection sampling, bytes ≥ 248 discarded) + 6 Base62 CRC32-IEEE checksum (big-endian, 0-padded). Parse accepts only the canonical spelling and compares the checksum as text (no second spelling of the same CRC); every defect is the one ErrShape.
  • Hash = SHA-256 hex (same procedure as knowledge keys, fits the migration-161 CHECK), HashPrefix (limiter key for W01-6), Hint = ctxm_… + last 4 checksum characters, HasPrefix (the knowledge paths' switch).
  • Deviation from design/01 §4.1 with reason: String(), GoString() and LogValue() return the hint, Plaintext() is the only way to the secret, the plaintext sits behind a pointer so fmt by reflection prints an address, not the body; json.Marshal yields {}.
  • depguard rule modelkey-leaf (stdlib only; math/rand and math/rand/v2 denied explicitly), 14 deadcode-testonly lines with their consuming wave, docs/security.md section "Model API key format" incl. secret-scanning pattern.

Gates (red against 6773d5b, then green)

  • Compile: no Go files → ok.
  • Every single-character flip of a golden key (3 299 cases): Parse without the checksum compare accepts 2 989 → 0.
  • Checksum alias (CRC + 2³², six digits): accepted by a numeric decoder → rejected.
  • Rejection sampling: b % 62 without discard χ² ≈ 28 000 → ≈ 50 (limit 150); stepped reader with bytes 248–255 never yields a digit.
  • Leak paths (fmt %v %+v %#v %s %q %x, slog text/JSON, json): design String() leaks on 10 of 14 → 0.
  • depguard overlay import of a ctx package: 0 findings → finding. deadcode testonly: 14 NEW → ok (150).
  • Fuzz 60 s: 2.2 M execs PASS. Golden vector 0KG44D / ctxm_…G44D recomputed independently with Python.
  • Second review (fresh Opus, main context): 4.1 M one-byte edits, 0.94 M adjacent swaps, 12 238 checksum aliases all rejected; χ² 80.8 over 2.15 M characters; 276 leak paths, only the documented reflect path shows the body; 18/20 mutants killed. One Low: two loop-boundary mutants survived → second commit adds a per-position invalid-character probe with a valid checksum (red: 194 of 8 342 accepted at positions 5 and 47 with the mutants, green after).
  • Build, vet (integration tag), golangci-lint ×2, deadcode ×2, go test -short -race ./... (66 ok; internal/llm TestChatBodyCap is a known host-load flake, package untouched).

Hand-offs

  • W01-4 / W01-5 / W02-05 delete their deadcode-testonly lines; W01-6 uses HashPrefix as limiter key.
  • W01-4: HasPrefix does not see ​/ before the prefix — such input goes down the knowledge path (one query, 401); no bypass.

Wave report .project/plan-openai-api-2026-11-03/reports/bau/W01-2.md, review reports/review/W01-2-fremdreview-2.md.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a secure model API key format with checksum validation, consistent error handling for invalid keys, and masked display output.
    • Added key hashing and short display hints for safer storage and presentation.
  • Documentation
    • Documented the key format, generation and validation behavior, and planned availability in a future release.

GottZ and others added 2 commits November 3, 2026 21:35
… hash and hint (W01-2)

Wave W01-2 of the OpenAI-compatible API layer (RC-B): the new stdlib-only
leaf package internal/modelkey defines the credential format of the model
API. No caller outside the package yet, no migration, no live effect.

- Format: "ctxm_" + 43 Base62 body characters (256 bits from crypto/rand,
  rejection sampling: bytes >= 248 are discarded) + 6 Base62 checksum
  characters (CRC32-IEEE over prefix and body, big-endian, '0'-padded)
  = 54 bytes.
- Parse accepts only the canonical spelling: no trimming, the checksum is
  compared as text (no second spelling such as CRC+2^32), and every defect
  returns the one sentinel ErrShape.
- Hash = hex(sha256(key)) like knowledge keys; HashPrefix = its first
  8 bytes (failure-limiter key for W01-6); Hint = "ctxm_…" + the last four
  characters, all of them checksum characters.
- Deviation from design/01 §4.1: String() returns the hint, not the
  plaintext; Plaintext() is the one method that returns the secret;
  GoString and LogValue return the hint too, and the plaintext sits behind
  a pointer, so fmt's reflection paths (non-string verbs, unexported struct
  fields) print an address instead of the key.
- depguard rule modelkey-leaf (leaf rule 7/7): stdlib only, plus explicit
  denies for math/rand and math/rand/v2 (a seeded ChaCha8 passes every
  black-box test). 14 lines in deadcode-testonly-allow.txt (waiting for
  W01-4, W01-5, W02-05; three implicit hint-rendering methods).
  docs/security.md "Model API key format".

Gates red -> green:
- compile: `go test ./internal/modelkey/` "directory not found", then
  "undefined: Prefix" with the tests alone -> ok
- single-character flips: a Parse without the checksum comparison accepts
  2989 of 3299 flips -> 0 of 3299
- aliased checksum: a uint32-decoding Parse accepts CRC+2^32 -> rejected
- shape with a recomputed checksum: Parse without the prefix check, without
  the character check, or with "_" allowed accepts ctxt_/CTXM_/NUL/0xFF/_
  inputs -> rejected
- rejection sampling: b % 62 without rejection gives the body
  "012345670123…" and chi² ≈ 28 000 over 10^5 keys -> golden body,
  chi² ≈ 50 (bound 150)
- leak gate: the design's String() leaks the body on 10 of 14 paths, a
  string field with hint methods on 3 -> 0; hint output pinned exactly
- depguard: an overlay import of internal/util, and Generate drawing from
  math/rand/v2, lint clean without the rule -> refused by modelkey-leaf
- deadcode testonly: 14 NEW unreachable symbols -> ok (150 entries)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… a valid checksum (W01-2, review 2 F-1)

Second-review finding F-1 (low, test gap, no production defect): the single
character-check probe with a recomputed checksum sat at body position 20, so
a Parse loop that skips the first or the last body position survived the
whole suite and 20 s of fuzzing — every other flip failed at the CRC
comparison first.

- TestParseRejectsInvalidCharAtEveryBodyPosition: positions 5…47 × the 194
  bytes outside [0-9A-Za-z], each with the checksum recomputed over prefix
  and body (8342 inputs) — the refusal can only come from the character
  check.
- FuzzParse: every accepted input must also match ^ctxm_[0-9A-Za-z]{49}$, an
  oracle spelled independently of alphabet and isBase62.
- withSum is now a shared test helper.
- docs/security.md: the reflection output is an address in braces or a
  %!verb(*string=0x…) marker (review 2 F-5, wording only).

Red -> green (mutants on Parse, full package suite):
- loop start len(Prefix)+1: ok -> FAIL TestParseRejectsInvalidCharAtEveryBodyPosition,
  "accepted 194 of 8342 invalid-character inputs (want 0 of 8342)"
  (first line "byte 0x00 at 5 with a valid checksum: err = <nil>")
- loop end Len-sumLen-1: ok -> FAIL, same test, "accepted 194 of 8342",
  first line "byte 0x00 at 47 with a valid checksum: err = <nil>"
- loop end Len-1 stays green: an equivalent mutant — position 53 is a
  checksum digit, checksum() emits only Base62, so a foreign byte there
  always fails the string comparison.
- unmutated: go test -short -race -count=1 ./internal/modelkey/ ok; lint
  both forms 0 issues; deadcode both ok (1 / 150 entries); fuzz 20 s PASS.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 3, 2026 20:58
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Walkthrough

Adds a Go package for generating, parsing, hashing, and safely displaying model API keys. It also adds tests, lint restrictions, and security documentation. The documentation states that key issuance and /v1 acceptance are not part of this release.

Changes: Model API key

Layer / File(s) Summary
Key format, generation, and parsing
go/internal/modelkey/modelkey.go, go/internal/modelkey/modelkey_test.go, .golangci.yml, docs/security.md
Defines the key format, generation, parsing, and prefix check. Tests cover malformed keys, checksum validation, rejection sampling, and generated-key properties. Adds a lint rule restricting imports in internal/modelkey. The security documentation describes the format and related handling rules.
Key accessors and safe formatting
go/internal/modelkey/modelkey.go, go/internal/modelkey/modelkey_test.go, go/deadcode-testonly-allow.txt
Adds hash, hash-prefix, and hint accessors. String, Go-syntax, and slog formatting use the hint. Tests check zero values, formatting output, and hash behavior; the allowlist records the new symbols.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Possibly related PRs

  • GottZ/ctx#74: Adds the preceding llmusage-leaf depguard rule and numbering that this change extends.
Merge Risk: Minimal · up to c628d

The standalone key-format package is mergeable after normal checks; no actionable risk remains from this review.

Security architecture risk: Minimal · up to c628d

The new credential package has no production callers in this change. Its generation, validation, and ordinary formatting controls are explicit, and the review found no material security risk introduced by this PR. Future issuance, authorization, and rate-limiting integrations remain outside this assessment.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The effective exposure in this PR is the new library contract, not an independently attackable service or tenant boundary. No production caller was established, and no issuance or acceptance path grants privileges through this package yet.

Trust Boundaries and Controls

  • observed — Attacker-supplied token text reaches fixed-length, prefix, alphabet, and canonical-checksum checks before a Key is returned. These checks neither consult identity nor authorize access. Secret generation uses crypto/rand rather than a caller-selected random source through the exported API.
  • observed — The redaction design separates ordinary display from explicit secret access. Its boundary is limited: reflection-based dumpers can follow the private pointer, and callers can expose Plaintext. The package documents these limitations; redaction is not an in-process secrecy sandbox.

Resilience and Maintainability Implications

  • observed — Credential construction publishes only completed values, and malformed parsing returns an empty value. The implementation has no persistent writes or mutable registry requiring rollback, cleanup, reservation recovery, or cross-request ownership coordination.
Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 55.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title names the main change: a new ctxm_ model-key package (internal/modelkey) with a Base62 body, CRC32 checksum, hash, and hint. The changeset adds that package, its tests, and the security doc …
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title names the main change: a new ctxm_ model-key package (internal/modelkey) with a Base62 body, CRC32 checksum, hash, and hint. The changeset adds that package, its tests, and the security doc section. The title is long (91 characters) and uses a parenthetical work-item tag, but it is specific and matches the changeset.

Full details: Docstring Coverage

Explanation

Docstring coverage is 55.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 20 functions across 2 files. (3 skipped: 3 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.
Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2483de15-c2b9-4530-ba3f-81f5b6fd7e72
Commits

Reviewing files that changed from the base of the PR and between ca3fbd1 and c628d32.

Files selected for processing: 5
  • .golangci.yml
  • docs/security.md
  • go/deadcode-testonly-allow.txt
  • go/internal/modelkey/modelkey.go
  • go/internal/modelkey/modelkey_test.go

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Copilot was unable to complete this review at the selected tier due to model policy or plan restrictions.

🔵 Needs a closer look

This change introduces a new security-sensitive credential/key format (ctxm_) along with linting/policy updates, so it warrants a focused human security review to confirm leak-safety, format correctness, and interoperability with planned callers.

No open issues 🎉

What changed in this PR

Adds a new stdlib-only leaf Go package internal/modelkey defining the external-facing ctxm_ model API key format (Base62 body + CRC32 checksum), along with hashing/hinting utilities, extensive validation/leak-prevention tests, and supporting repo guardrails/documentation for staged rollout.

Changes:

  • Implement ctxm_ key generation/parsing, SHA-256 hashing (and prefix), and hint-only rendering/logging behavior (String/GoString/LogValue).
  • Add comprehensive tests (golden vectors, mutation/alias rejection, uniformity gates, fuzzing, formatting/logging/JSON leak checks).
  • Update docs/security.md with key-format + secret-scanning guidance; add depguard leaf rule and deadcode-testonly allowlist entries.
File Summary
go/​internal/​modelkey/​modelkey.go Implements the ctxm_ model key type, parsing/generation, hashing, and hint-safe rendering/logging behavior.
go/​internal/​modelkey/​modelkey_test.go Adds golden-vector, rejection/mutation, uniformity, leak-prevention, and fuzz tests for model keys.
go/​deadcode-testonly-allow.txt Temporarily allowlists the new leaf API surface until downstream waves introduce production callers.
docs/​security.md Documents the model API key format and secret-scanning guidance for ctxm_… keys.
.golangci.yml Adds a depguard rule enforcing internal/modelkey as a stdlib-only leaf (explicitly denies math/rand imports).

Review effort: Balanced

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