Skip to content

fix(cfg,token): remove hardcoded brand identity literals#6

Merged
devonartis merged 1 commit into
developfrom
fix/td-token-001-remove-issuer-hardcode
Apr 10, 2026
Merged

fix(cfg,token): remove hardcoded brand identity literals#6
devonartis merged 1 commit into
developfrom
fix/td-token-001-remove-issuer-hardcode

Conversation

@devonartis

Copy link
Copy Markdown
Owner

Summary

Removes every brand-coupled string literal from internal/cfg/ and internal/token/ (production + tests). The JWT iss claim, the JWT aud override, the TrustDomain default, the DBPath default, and the FHS config search paths all hardcoded "agentauth" (or "agentauth.local", "./agentauth.db", /etc/agentauth/...) into source. This PR makes them configurable, defaults them to brand-neutral values, and migrates the test surface to brand-neutral fixture values in one pass.

Resolves: TD-TOKEN-001, TD-TOKEN-002, TD-TOKEN-003, TD-CFG-001, TD-CFG-002, TD-TEST-001 (all marked RESOLVED in TECH-DEBT.md).

What changed

Production:

  • cfg.Issuer field added (env AA_ISSUER); empty = skip issuer enforcement on Verify() (mirrors documented Audience contract)
  • Issuer check moved out of TknClaims.Validate() (now pure structural — sub, jti, exp, nbf only) into TknSvc.Verify() where cfg is available
  • cfg.go:96 Audience override "" -> "agentauth" deleted; honors documented "empty = skip" contract
  • TrustDomain default "agentauth.local" -> "agentwrit.local"
  • DBPath default "./agentauth.db" -> "./data.db"
  • Config search paths /etc/agentauth/config -> /etc/broker/config, ~/.agentauth/config -> ~/.broker/config
  • Generated config file header # AgentAuth Configuration -> # Broker Configuration
  • Package doc comments in tkn_claims.go updated (no more "the issuer is always 'agentauth'")

Tests:

  • Test fixtures across cfg/, token/, authz/, deleg/, admin/, identity/, mutauth/ updated to use brand-neutral values: Issuer: "test-issuer", TrustDomain: "test.local", spiffe://test.local/...
  • 6 locked-in assertions in tkn_svc_test.go and val_mw_test.go now drive from fixture cfg instead of literals
  • 3 cfg.Cfg{} literal constructions in tkn_svc_test.go, deleg_svc_test.go, admin_svc_test.go now include Issuer: "test-issuer"
  • TestLoad_AudienceDefault now asserts empty-when-unset (the actual contract)
  • TestLoad_DBPathDefault now asserts ./data.db

Root cause

IssuerURL was an OIDC-coupled cfg field stripped during the open-core split. The strip removed the field, the validation, AND the config-driven tests (tombstone preserved at internal/token/tkn_svc_test.go:521), but the validation was replaced with a hardcoded "agentauth" literal rather than left configurable. The general JWT iss claim is independent of OIDC and core still needs it. This PR restores configurability without re-coupling to OIDC.

Full audit at .plans/reviews/2026-04-10-hardcoded-identity-audit.md.

Standing rule

~/.claude/CLAUDE.md now contains "No Hardcoded Identity Values — Universal, Non-Negotiable" as a global rule across all projects. Identity-shaped string literals in source code are now non-negotiable findings.

Test plan

  • go build ./... clean
  • go vet ./... clean
  • gofmt -l internal/ cmd/ returns empty
  • go test ./internal/... — 14/14 internal packages pass locally
  • CI: format, build, vet, lint, unit-tests, unit-tests-race, gosec, govulncheck, go-mod-verify, contamination, docker-build, smoke-l25, sbom all green
  • CI: gates-passed aggregator green (required for merge)
  • Behavioral spot-check after merge: boot broker with AA_ISSUER=foo, issue a token, verify iss claim is "foo". Boot without AA_ISSUER, verify the iss check is skipped on Verify.
  • Confirm no acceptance test env.sh files broke (they don't set AA_ISSUER, but that's fine — empty = skip)

What this PR does NOT do

  • Does NOT add fail-fast validation for missing config (operator responsibility)
  • Does NOT touch aactl init interactive flow (separate work)
  • Does NOT update example JWT payloads in user-facing docs (docs/api.md, docs/common-tasks.md, docs/foundations.md, docs/implementation-map.md still show "iss": "agentauth" — separate cleanup item)
  • Does NOT rename aactl -> awrit binary (TD-CLI-001, separate PR)
  • Does NOT add a config-matrix CI job to verify tunables at runtime (deferred — flagged in the action plans for the next PR that adds a tunable)

…1/002/003, TD-CFG-001/002, TD-TEST-001)

The JWT iss claim, JWT aud override, TrustDomain default, DBPath default,
and config search paths all hardcoded brand-coupled string literals into
source. The "no hardcoded identity values" rule says these belong in cfg,
not in literals. This commit removes every brand literal from the production
surface and the test surface in one pass.

Production code:
- iss: hardcoded "agentauth" -> driven by cfg.Issuer (env AA_ISSUER).
  Issuer enforcement moved out of TknClaims.Validate() (now pure structural)
  into TknSvc.Verify() where cfg is available. Empty cfg.Issuer skips the
  check, mirroring the documented Audience contract — operator opt-in.
- aud: cfg.go:96 override "" -> "agentauth" deleted. Audience now honors
  its own documented "empty = skip" contract. No brand-coupled default.
- TrustDomain default: "agentauth.local" -> "agentwrit.local"
- DBPath default: "./agentauth.db" -> "./data.db"
- Config search paths: /etc/agentauth/config -> /etc/broker/config,
  ~/.agentauth/config -> ~/.broker/config, generated config file header
  updated.
- Package doc comment in tkn_claims.go updated to stop teaching that
  the issuer is "always 'agentauth'".

Test surface:
- All test fixtures across cfg/, token/, authz/, deleg/, admin/, identity/,
  mutauth/ updated to use brand-neutral test values: test-issuer for cfg,
  test.local for trust domain, spiffe://test.local/... for SPIFFE URIs.
- Test assertions that locked in the hardcoded literals (cfg_test.go,
  tkn_svc_test.go, val_mw_test.go) now drive from fixture cfg, not literals.
- TestLoad_AudienceDefault now asserts empty-when-unset (skip contract),
  not "agentauth"-when-unset.
- TestLoad_DBPathDefault now asserts ./data.db.

Root cause: IssuerURL was an OIDC-coupled cfg field stripped during the
open-core split. The strip removed the field, the validation, AND the
config-driven tests (tombstone preserved at internal/token/tkn_svc_test.go:521),
but the validation was replaced with a hardcoded literal "agentauth"
rather than left configurable. The general JWT iss claim is independent
of OIDC and core still needs it — this commit restores configurability
without re-coupling to OIDC.

Standing rule added: ~/.claude/CLAUDE.md "No Hardcoded Identity Values —
Universal, Non-Negotiable" (global, all projects).

Validation: go build ./..., go vet ./..., gofmt -l, go test ./internal/...
all green. 14/14 internal packages pass.

Marks RESOLVED in TECH-DEBT.md:
- TD-TOKEN-001, TD-TOKEN-002, TD-TOKEN-003
- TD-CFG-001, TD-CFG-002
- TD-TEST-001

Full audit at .plans/reviews/2026-04-10-hardcoded-identity-audit.md.
@devonartis devonartis merged commit 2eb9094 into develop Apr 10, 2026
17 checks passed
@devonartis devonartis deleted the fix/td-token-001-remove-issuer-hardcode branch April 10, 2026 18:47
devonartis added a commit that referenced this pull request Apr 10, 2026
Catches docs up with the configuration defaults landed in PR #6 (TD-TOKEN-001,
TD-CFG-001, TD-CFG-002) and PR #7 (TD-010). Docs were previously teaching
the old hardcoded brand values in example JWT payloads, SPIFFE URIs,
filesystem paths, and env var defaults. Now they reflect the current
defaults as shipped.

Direct push to develop (not PR) per explicit user instruction — this is
documentation chasing production defaults, not new code. No Go source
changes.

Sweep patterns (bulk sed across docs/**/*.{md,yaml,yml,svg}):
- agentauth.local        -> agentwrit.local    (matches cfg.TrustDomain default)
- agentauth.db           -> data.db            (matches cfg.DBPath default)
- /etc/agentauth/        -> /etc/broker/       (matches cfg.configfile.go search path)
- /var/lib/agentauth/    -> /var/lib/broker/   (consistent data-dir naming)
- ~/.agentauth/          -> ~/.broker/         (matches cfg.configfile.go home fallback)
- "iss": "agentauth"     -> "iss": "agentwrit" (example JWT payloads)
- "aud": "agentauth"     -> "aud": "agentwrit" (example JWT payloads)
- AgentAuth              -> AgentWrit          (brand prose)
- agentauth (bare)       -> agentwrit          (brand prose)
- AGENTAUTH              -> AGENTWRIT          (any uppercase)

Preserved (not changed):
- github.com/devonartis/agentauth — Go module path + GitHub clone URLs
  (getting-started-user.md lines 74, 104). Module path rename is gated on
  the GitHub repo rename, which is separate work.
- CHANGELOG historical entries (not touched)
- tests/*/evidence/*.md historical records (not touched — they're in
  tests/ not docs/, so the sweep didn't reach them anyway)

Renames:
- docs/agentauth-explained.md -> docs/agentwrit-explained.md

Targeted updates:
- docs/credential-model.md:154 — the "hardcoded adminTTL = 300, see TD-010"
  text replaced with the new reality: "5 minutes default, operator-configurable
  via AA_ADMIN_TOKEN_TTL." TD-010 is now RESOLVED in TECH-DEBT.md so the
  reference was stale.

Scope: ~20 doc files + 1 file rename. No code, no tests, no scripts.
devonartis added a commit that referenced this pull request Apr 12, 2026
Includes:
- PR #6: hardcoded identity audit fixes (TD-TOKEN-001/002/003, TD-CFG-001/002, TD-TEST-001)
- PR #7: adminTTL → cfg.AdminTokenTTL (TD-010)
- PR #8: aactl → awrit binary rename (TD-CLI-001)
- PR #11: docs audit P1/P2 corrections + docs-layout-archon-style sweep
- PR #12: runtime rebrand alignment — problemdetails URN, obs metrics,
  awrit init config path (TD-RUNTIME-001, TD-OBS-001, TD-CLI-002),
  GitHub repo rename agentauth → agentwrit, Go module path rename,
  README/CONTRIBUTING/docs sweep, CI brand-alignment gate
- Direct-to-develop: brand sweep docs (684083b)

Stripped 16 dev-only paths: MEMORY.md, FLOW.md, TECH-DEBT.md,
MEMORY_ARCHIVE.md, CLAUDE.md, AGENTS.md, COWORK_SESSION.md,
COWORK_DOCS_AUDIT.md, .plans/, .claude/, .agents/, audit/, adr/,
.vscode/, .active_module, generate_pdf.py.
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.

1 participant