Skip to content

feat(beltic): end-to-end Beltic VC integration — identity + agent authorization - #278

Open
alivia-beltic wants to merge 19 commits into
gethouston:mainfrom
alivia-beltic:worktree-beltic-integration
Open

feat(beltic): end-to-end Beltic VC integration — identity + agent authorization#278
alivia-beltic wants to merge 19 commits into
gethouston:mainfrom
alivia-beltic:worktree-beltic-integration

Conversation

@alivia-beltic

Copy link
Copy Markdown

Summary

Adds Beltic verifiable-credentials issuance to Houston end-to-end:

  • Identity credentials — self-attested user identity issued via Beltic, with optional supporting documents (passport / driver's-license / national ID / residence permit) hashed locally and bound to the JWT-VC via evidence_refs.
  • Agent authorization credentials — delegated trust from a verified identity to an individual agent, with FinCEN AML constraints (delegated_by_subject_id), spending limits, and a freshly-minted ES256 did:jwk subject + private keypair persisted at ~/.houston/workspaces/<W>/<Agent>/.houston/agent_did/agent_did.json (mode 0600).
  • UI — Settings → Identity (with verify modal + Verified status panel) and Settings → Authorized agents (with consent dialog). Mission Control agent cards grow a "Verified by Beltic" tag when a credential is active.

Targets the Beltic staging API (api.staging.beltic.com/v1); a dev-only cfg!(debug_assertions) block in app/src-tauri/src/lib.rs injects the staging key.

Pairs with beltichq/platform PR #179 which adds the evidence storage primitives (Prisma Evidence model + EvidenceRepository + S3 storage service + credentials:evidence:upload permission). When that PR's follow-up (Hono route + JWT evidence[] embedding) lands, Houston will swap the opaque sha256:... evidence refs for evidence:<id> refs in a one-line change.

What's in this PR (commits, oldest → newest)

# Commit What it does
1 cad9f16 houston-beltic crate: REST client, issuer (issue/revoke), local JWT-VC verifier (JWKS + Status List 2021), webhook HMAC verifier
2 e75918b houston-engine-core credentials module: persist VCs in .houston/credentials/, identity helpers at workspace root
3 45ae465 engine-server credentials + webhook routes; HoustonEvent::Credential* for UI invalidation
4 850d79c Cargo.lock bump
5 0875eb5 @houston-ai/engine-client TS types + methods + agent-credentials TanStack Query hooks
6 0bf62e3 Settings → Identity + Authorized agents sub-nav (registered into SettingsView)
7 1e58567 Agent authorization consent dialog (declarations, spending limits, confirmation modes)
8 052ef3e "Verified by Beltic" tag on Mission Control agent cards
9 7e2e6ec Identity issuance route + verify modal + populated Identity pane
10 4a14874 Wire delegated_by_subject_id to live identity credential; bake staging Beltic key for dev
11 edbfa52 Mint real ES256 did:jwk for agents on credential issuance; persist private JWK 0600
12 2769416 Wire Identity + Authorized agents into Settings sidebar; fix verify dialog i18n keys
13 616948c Verify dialog redesign with drag-drop document evidence + local SHA-256 hashing; Identity panel "Verified with" row; en/es/pt i18n

Architecture highlights

  • Files-first reactivity: every credential is persisted under .houston/ and surfaced via TanStack Query keys invalidated by HoustonEvent::Credential*. A separate file watcher catches bypass writes so direct edits show up in the UI without refresh.
  • Engine boundary: all Beltic logic lives in the transport-neutral houston-beltic crate. The engine-server thin-wraps it; the app side has no Beltic dependency, only the engine-client TS types.
  • Library boundary: zero Beltic-aware code in ui/@houston-ai/*. The consent + verify dialogs and identity sections live in app/src/components/settings/ per the project-rule that domain-specific UI stays in app/.
  • No silent failures: every Beltic error variant (auth, rate limit, quota, transient) maps through BelticError → CoreError → ApiError → errorMessage(err) → toast. Beta-stage policy: surface, don't swallow.

Out of scope (next session)

  • Engine route to mirror evidence bytes to ~/.houston/workspaces/<W>/.houston/identity/evidence/<sha256>.<ext> for local offline audit
  • Swap evidence ref format from sha256:... to evidence:<id> once beltichq/platform PR Provider config loading fails with type mismatch error #179 + its follow-up land in staging
  • Mission Control purchase-needs-approval card variant (deferred as product question)

Test plan

  • cargo test --workspace — all crates green
  • cd app && pnpm tsc --noEmit — clean (relay typecheck errors are pre-existing, unrelated)
  • cd app && pnpm check-locales — en/es/pt in sync, no em dashes
  • cargo build -p houston-engine-server — staged sidecar fresh
  • Dev pnpm tauri dev — Verify identity flow round-trips against api.staging.beltic.com/v1, agent authorization mints a real did:jwk and persists 0600 private JWK
  • Reviewer manual: drop a PDF + JPG into the verify dialog, confirm sha256 hashes appear in evidence_refs[] on the resulting credential row
  • Reviewer manual: authorize an agent, confirm .houston/agent_did/agent_did.json exists with mode -rw------- and a valid P-256 keypair

🤖 Generated with Claude Code

sajc11 and others added 15 commits May 22, 2026 16:43
…verifier

New transport-neutral Rust crate wrapping Beltic's Credentials API. Models
on houston-composio (third-party-integration reference). 35 inline tests,
all green; no Tauri / React / axum coupling.

- errors.rs — typed BelticError mirroring Beltic's nested `{ error: {code,
  message} }` envelope; from_envelope() mapper; is_retryable() for backoff
- config.rs — Configuration::from_env(); jwks_url() / status_list_url()
  derived from BELTIC_BASE_URL so local Beltic just works
- client.rs — reqwest wrapper with X-Api-Key, JSON in/out, parses the
  actual Beltic error envelope
- issuer.rs — issue / revoke / get + client-side FinCEN guard:
  delegated_by_subject_id required on any wallet-scoped agent permission
- webhook_verifier.rs — Stripe-pattern Beltic-Signature: sha256=<hex>
  + Beltic-Timestamp with 300s replay window; constant-time compare
- verifier/ — split into mod/jwks/status_list/policy (200-line rule):
  - jwks: cache w/ Cache-Control max-age, ES256 P-256 signature verify
  - status_list: W3C Status List 2021 gzip + base64url bit lookup
  - policy: lte/lt/gte/gt/eq/neq/in operators on permission conditions
Stores Beltic-issued credentials per agent (agent_authorization) and at
the workspace level (user identity). Files-first per knowledge-base/
files-first.md: append-only Vec<VerifiableCredential> in
.houston/credentials/credentials.json (agent) or
.houston/identity/identity.json (workspace), reactive via the file
watcher + WS event invalidation in chunk 3.

- types.rs — VerifiableCredential (Beltic subject_id, claims, signed JWT,
  delegated_by_subject_id, status_list_index), CredentialStatus enum
  (Active/Suspended/Revoked/Expired)
- store.rs — agent-scoped list/save/active/find_by_credential_id/
  update_status. save() rejects duplicates by credential_id so retried
  issuance jobs don't produce phantom rows. update_status() auto-stamps
  revoked_at when transitioning to Revoked.
- identity.rs — workspace-scoped equivalent for the user's `user`
  credential. Same shape; different folder.

22 inline tests passing (9 mine + 13 pre-existing in adjacent modules).
…ential*

Wires houston-beltic into the engine: REST + WS exposure. Events route
to `agent:{agent_path}` so the existing per-agent subscription picks them
up alongside ActivityChanged/SkillsChanged/etc.

- houston-ui-events: 3 new HoustonEvent variants — CredentialIssued,
  CredentialRevoked, CredentialSuspended (all carry agent_path +
  credential_id)
- houston-engine-protocol::event_topic: routes the new variants to
  `agent:{agent_path}` so existing UI subscriptions get them
- routes/credentials.rs:
    GET   /v1/agents/credentials?agent_path=...           list local
    POST  /v1/agents/credentials?agent_path=...           issue via Beltic
    POST  /v1/agents/credentials/:id/revoke?agent_path=…  revoke via Beltic
    POST  /v1/agents/credentials/:id/verify?agent_path=…  local JWT-VC verify
- routes/webhooks_beltic.rs:
    POST  /v1/webhooks/beltic                              receive + verify
                                                          HMAC + propagate
                                                          status change to
                                                          the matching agent
- routes/beltic_shared.rs: lazy OnceLock-backed BelticContext (Client +
  Issuer + Verifier from env) and map_beltic() error translator. Shared
  by both route files.
- houston-beltic::IssueRequest now `Deserialize` so axum can parse it
  from JSON. WebhookVerifier gains associated constants
  SIGNATURE_HEADER/TIMESTAMP_HEADER for ergonomic access in handlers.

255 workspace tests passing (houston-beltic 35 + houston-engine-core 220).
Mirrors the Rust DTOs on the TS side so the desktop app can talk to the
new credentials routes. Wire format snake_case (Beltic's convention) —
no transform layer.

- ui/core/src/types.ts: HoustonEvent union gains CredentialIssued /
  CredentialRevoked / CredentialSuspended variants (all carry
  agent_path + credential_id, matching the Rust event payload)
- ui/engine-client/src/types.ts: VerifiableCredential, CredentialStatus,
  IssueCredentialRequest, VerifyCredentialResult
- ui/engine-client/src/client.ts: 4 new HoustonClient methods —
  listAgentCredentials / issueAgentCredential / revokeAgentCredential /
  verifyAgentCredential — wrapping the engine REST surface
- app/src/hooks/queries/use-agent-credentials.ts: TanStack Query hooks
  (useAgentCredentials, useActiveAgentCredential,
  useIssueAgentCredential, useRevokeAgentCredential,
  useVerifyAgentCredential). Mutations surface failures via
  showErrorToast() with Report-bug action per CLAUDE.md no-silent-failures
- app/src/hooks/use-agent-invalidation.ts: wire the 3 new events to
  invalidate the ["agent-credentials", agentPath] query key
Adds two new Settings sub-nav items per the v2 Figma design (which I
realised matches the actual 3-pane Settings layout the app already uses,
not a card-heavy standalone page).

- settings-view.tsx: 2 new SettingsSectionId variants ("identity",
  "agents"); 2 new nav items (ShieldCheck + Users icons); 2 new render
  branches for the section components
- sections/identity.tsx: empty-state pane with a "Verify identity" CTA.
  Disabled for now — workspace-identity routes land in a follow-up
  alongside the consent modal
- sections/agents.tsx: lists every agent in the current workspace via
  useAgentStore, one row per agent
- sections/agents-row.tsx: pulls per-agent credential via the chunk-4
  useActiveAgentCredential hook, shows status pill, short credential id,
  delegated_by_subject_id chain, and a revoke action. Confirm prompt
  before mutate; tone-colored status pill
- locales en/es/pt: settings.nav.{identity,agents} +
  settings.identity.* + settings.agents.* keys. check-locales clean,
  no em dashes per i18n rule

`pnpm tsc --noEmit` clean. Mission Control card variants land in a
follow-up; they need extending @houston-ai/board status enum and are a
chunkier change.
Matches the Figma "Authorize this agent" screen. Opens from the
Authorize button on each agent row in Settings → Authorized agents.
Closes on successful issuance; the WS event invalidator (chunk 4)
refreshes the row's status pill.

- authorize-agent-dialog.tsx: modal with spend-limit fields (daily +
  per-transaction), currency chips (USD/BRL/EUR/GBP), ISO-8601 idle
  timeout, 3-mode confirmation rules (always / threshold / never) with
  inline threshold input, declaration checkbox. Submits via the
  useIssueAgentCredential mutation from chunk 4.
- agents-row.tsx: when an agent has no active credential, shows an
  Authorize button that launches the modal. When it has one, shows the
  Revoke button (same behaviour as before).
- agents.tsx: passes the new agentId prop down.
- locales en/es/pt: settings.agents.consent.* keys covering every
  string in the modal. check-locales clean.

Known placeholders (called out in code comments) — chunk 8 follow-ups:
- subject.id is `did:jwk:houston-<agentId>` for now; a real ES256
  keypair (generated server-side, key stored encrypted) lands with the
  identity flow
- claims.delegated_by_subject_id is `usr_houston_local` placeholder;
  pulls from the workspace identity credential once chunk 8 wires
  identity routes
Mission cards now render a "Verified by Beltic" tag when their owning
agent has an active agent_authorization credential. Cheapest possible
touchpoint: uses the existing KanbanItem.tags array (which the board
already renders as gray pills below the card) — no library boundary
crossing into @houston-ai/board.

- use-verified-agent-paths.ts: cross-agent fan-out via TanStack
  useQueries. Returns the Set<string> of agent paths whose newest
  agent_authorization row is active. 30s staleTime since Mission
  Control isn't the credentials-focused surface (and the WS event
  invalidator keeps it honest anyway)
- use-mission-control.ts: looks up each card's owning agent in the
  verified set and appends the tag when present

A future enhancement is a Beltic-tinted tag tone — that'd take a
`tagTone` prop on KanbanItem and is left for a follow-up so this chunk
stays inside the app boundary.
End-to-end Beltic user-credential flow: route → hooks → modal → wired
Identity Settings pane.

- engine-server/src/routes/identity.rs:
    GET   /v1/identity         current Beltic user credential or null
    POST  /v1/identity         issue (build Beltic IssueRequest from
                               nationality, dob, id document fields and
                               persist via houston_engine_core::credentials::identity)
    POST  /v1/identity/revoke  call Beltic revoke + update_status
  Auto-promotes trust_level to "idv_verified" when an id_document_type
  is set; validates that an ID type without a country is rejected
  client-side (matches Beltic schema constraint).
- ui/engine-client: IssueIdentityRequest type + 3 HoustonClient methods
- app/src/hooks/queries/use-identity.ts: useIdentity / useIssueIdentity
  / useRevokeIdentity TanStack hooks. Mutations surface failures via
  showErrorToast() with Report-bug button per CLAUDE.md.
- app/src/hooks/use-agent-invalidation.ts: CredentialIssued/Revoked/
  Suspended events with agent_path prefixed "identity:" route to the
  workspace-identity query key instead of the per-agent one.
- app/src/components/settings/sections/identity.tsx: real state.
  Empty → shows verify CTA + opens the modal. Verified → renders
  status pill, trust level, credential id, issued/expires, and the
  Re-verify / Revoke buttons. Revoke uses window.confirm with the
  cascading-warning copy.
- verify-identity-dialog.tsx: matches the Figma "Verify Your Identity"
  fields (nationality, DOB, optional document type+country) with the
  three Beltic declarations folded into one consent checkbox. Submits
  via useIssueIdentity.
- locales en/es/pt: settings.identity.verify.* + new
  identity.revokeConfirm key. No em dashes.

The placeholder user_id from chunk 6 (delegated_by_subject_id =
"usr_houston_local") will be replaced in a follow-up that wires the
real user subject_id from this identity credential. Both routes work
independently for now — agents authorize fine without an identity, and
identity issues fine without any agents.
…g Beltic key for dev

Two related changes that finalise the agent_authorization flow for the
dev demo.

1. authorize-agent-dialog reads useIdentity and uses the active identity
   credential's subject_id as delegated_by_subject_id. If no identity is
   active, the modal shows an amber warning pointing to Settings →
   Identity → Verify, and the submit button is disabled. Previously the
   modal sent a "usr_houston_local" placeholder, which is the kind of
   thing Beltic's FinCEN AML check is meant to catch.

2. app/src-tauri/src/lib.rs now seeds BELTIC_API_KEY +
   BELTIC_BASE_URL into the engine sidecar env at spawn time, but ONLY
   in debug builds (cfg!(debug_assertions)). Release builds are stripped
   by the compiler — no literal staging key ships to users. A parent-
   shell `export BELTIC_API_KEY=…` still wins via a follow-up env
   pass-through block. Key has read/write/revoke/verify scopes, no
   delete; staging only.

locales en/es/pt: settings.agents.consent.identityRequired{Title,Body}
+ delegatingFrom. check-locales clean.
Houston UI sends a `did:jwk:houston-<agentId>` placeholder when issuing
an agent_authorization credential. The engine route now detects that
placeholder, mints a fresh ES256 (P-256) keypair, builds a canonical
did:jwk, attaches the public JWK to the subject, and persists the
private JWK at `.houston/agent_did/agent_did.json` (mode 0600 on Unix)
before calling Beltic. Issuance success therefore always corresponds
to a keypair Houston can actually use later for presentation flows
(W3C VPs, AP2/UCP). Non-placeholder subjects and identity credentials
are left untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…x verify dialog i18n keys

Chunk 5 created the section files but never registered them in
SettingsView, so the new sub-nav was never visible. Adds them at the
top of the Settings sidebar (BadgeCheck + ShieldCheck icons) so users
can actually reach the Beltic flows.

VerifyIdentityDialog was calling `t("verify.title")` etc., but the
locale JSON nests those keys under `identity.verify.*`, so every label
rendered as the raw key path. Repoint all keys to `identity.verify.*`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ment

Replaces the placeholder dialog with a designed-system version and adds a
drag-drop file picker so users can attach supporting documents (passport,
driver's license, national ID, residence permit) when verifying their
identity. Each document is hashed locally with SHA-256; only the hash +
document type + filename leave the machine, encoded as opaque
`sha256:<hex>:<doctype>:<urlencoded-filename>` entries in
`evidence_refs[]`. Beltic stores those strings verbatim today; when the
Beltic /v1/evidence endpoint ships (see beltichq/platform PR gethouston#179), the
ref format will switch to `evidence:<id>` and the JWT-VC will gain a
W3C evidence[] claim with digestSRI.

App side:
- VerifyIdentityDialog rewritten to use @houston-ai/core Input + Select,
  a Card-styled drop-zone with hover/drag affordances, and per-file
  rows showing filename, size, sha256 prefix, doc-type picker, remove.
- Accepts PDF/JPG/PNG/WEBP/HEIC up to 10 MB. SubtleCrypto SHA-256 runs
  in the renderer; submit blocks while any file is still hashing.
- Identity panel grows a "Verified with" row that parses
  `sha256:...:...:...` refs and renders document type + filename + hash
  prefix per attached document.
- New i18n keys identity.verifiedWith, identity.noEvidence,
  identity.verify.{nationality,dob}Hint, identity.verify.evidence.*
  in en/es/pt. No em dashes per locale validator.

Engine side:
- IssueIdentityRequest gains an optional `evidence_refs: Vec<String>`,
  forwarded verbatim onto the Beltic IssueRequest.
- engine-client TS types updated on both VerifiableCredential
  (response) and IssueIdentityRequest (request).

Out of scope (next session, paired with Beltic PR gethouston#179 follow-up):
- Engine route to mirror evidence bytes to local
  `.houston/identity/evidence/<sha256>.<ext>` for offline audit
- Swap ref format from `sha256:...` to `evidence:<id>` once Beltic
  endpoint is live; render `evidence:<id>` entries by fetching metadata
  from Beltic instead of decoding the opaque string

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an engine route POST /v1/identity/evidence that takes raw bytes +
sha256 + content_type query params and writes them to
  <home>/.houston/identity/evidence/<sha256>.<ext>
(mode 0600 on Unix). The engine re-hashes the body and rejects on
sha256 mismatch so a buggy renderer can't poison a "trusted" content
address.

Verify dialog now persists every attached document via this route
BEFORE calling issueIdentity. If any persist fails (network, disk
full, hash mismatch), the issuance is aborted and the user sees a
toast — preferable to a credential whose evidence_refs name files
that aren't on disk.

This is the local-mirror half of the W3C evidence story. When Beltic's
/v1/evidence endpoint ships (beltichq/platform PR gethouston#179 follow-up), the
engine route will additionally upload these same locally-mirrored
bytes server-side, no client changes needed.

Added:
- engine-core::credentials::evidence_store — content-addressed writer
  with sha256 + content-type → path validation. 8 inline tests cover
  round-trip, path-traversal guards (rejects `..` in sha256), 0600
  permissions, content-type → extension mapping, overwrite semantics.
- Engine route handler verifies sha256 server-side via sha2 crate.
- HoustonClient.persistIdentityEvidence(bytes, {sha256, contentType})
  client method using application/octet-stream upload.
- Verify dialog holds File refs through hashing and POSTs bytes on
  submit. Issuance is aborted on any persist failure.
- New i18n key identity.verify.evidence.persistFailed in en/es/pt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a Reveal button per evidence row in the Identity panel. Clicking
it locates the locally-mirrored file by SHA-256 and opens Finder with
it selected.

The credential's evidence_refs[] carries the sha256 but not the
content_type the file was saved under, so the engine globs the
evidence dir for any `<sha256>.<anything>` match. Saved files include
the content-type-derived extension (.pdf, .jpg, etc.), so the glob
finds them regardless of the original filename.

Added:
- engine-core::credentials::evidence_store::locate_by_sha256(root, hex)
  → Option<PathBuf>. Validates the hex shape (rejects `..` and other
  non-hex chars) before touching the filesystem so a crafted query
  param can't traverse. 3 new tests bring the file to 11/11.
- Engine route GET /v1/identity/evidence?sha256=<hex> returning
  {path: "/absolute/path"} or 404.
- HoustonClient.locateIdentityEvidence(sha256) → {path}.
- Identity panel evidence row grows a FolderOpen icon button that
  resolves the path via the engine then calls osRevealPath (the
  existing Tauri command for Finder reveal / Explorer /select).
- New i18n keys identity.{revealEvidence,revealEvidenceAria,
  revealEvidenceFailed} in en/es/pt. No em dashes per locale validator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sajc11 and others added 4 commits May 25, 2026 16:02
…:<id> refs

End-to-end ref-format swap. When Beltic's /v1/evidence endpoint is
reachable (beltichq/platform PR gethouston#179 deployed to staging), the engine
now forwards attached identity-evidence bytes to Beltic alongside
the local mirror. The Beltic-returned `ev_<uuid>` is used as
`evidence:<id>` in the issued credential's evidence_refs[], which
triggers the W3C `evidence[]` block embedding in the JWT-VC (PR gethouston#179
chunk 11d).

Best-effort upstream: when the Beltic endpoint isn't reachable yet
(staging hasn't deployed), the engine logs at WARN and returns the
local-mirror path without `beltic_evidence_id`. The renderer falls
back to the existing `sha256:<hex>:<doctype>:<filename>` opaque
shape, which Beltic also stores verbatim. Re-running the same submit
once /v1/evidence is live is a no-op — Beltic's sha256-dedupe path
returns the existing resource on second upload.

houston-beltic crate:
- reqwest gains the `multipart` feature
- Client.post_multipart<Res>(path, form) — single multipart helper,
  inlined here rather than as a generic since evidence is the only
  consumer.
- Issuer.upload_evidence(bytes, content_type, document_type?,
  filename?) returns EvidenceResource. Both `document_type` and
  `filename` ride as multipart text fields per the Beltic schema
  (mirrors the curl example in the Mintlify guide).
- EvidenceResource type mirrors Beltic's flat JSON shape (ADR-002).

houston-engine-server:
- POST /v1/identity/evidence query params gain optional
  `document_type` and `filename` so the Beltic upload carries the
  metadata. Response gains optional `beltic_evidence_id`.
- After the local save succeeds, attempts the upstream upload via
  beltic_ctx().issuer.upload_evidence. Failures log at WARN and do
  NOT abort the route — the user's local copy is preserved either way.

App side:
- HoustonClient.persistIdentityEvidence gains `documentType` +
  `filename` args; return shape grows optional `beltic_evidence_id`.
- VerifyIdentityDialog uses the new return value: when Beltic
  responded with an id, ref is `evidence:<id>`; otherwise falls back
  to `sha256:<hex>:...`. The fallback is transparent and survives a
  later re-attempt.
- Identity panel `parseEvidenceRefs` recognizes both formats;
  `evidence:<id>` entries render with a truncated `evidence:` token
  instead of `sha256:`. Reveal-in-Finder button is hidden for
  evidence:<id> refs since the local-mirror key isn't recoverable
  from the ref alone (no sha256 inline).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a Select component above the drop-zone showing which document
type the next dropped file will be tagged as (default: passport).
Users change the type between drops to upload different categories
in one session. The drop-zone prompt echoes the current selection
so it's clear what the next drop becomes.

Per-row dropdown stays as a quiet safety net — most users will
rely on the pre-select, but if they drop something and realize it
was tagged wrong, they can fix it inline without removing and
re-adding the file.

i18n: new keys identity.verify.evidence.{nextDocType, tagAs} in
en/es/pt. tagAs is a templated string ("Tagged as {{type}}. Change
above to upload a different type.") so the live preview reads
naturally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds knowledge-base/beltic-integration.md documenting Houston's side of
the Beltic credentials integration, the wire-format fallback chain
(evidence:<id> preferred → sha256:<hex>:... fallback), and exactly
which Beltic PRs unblock which Houston features.

The Houston-side code is unchanged — this is just the deploy-dependency
documentation flagged as the last deferred item in the Beltic PR series.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Passive lockfile update — no Rust code changed this session. Captures
the mime_guess 2.0.5 transitive that picked up during incidental cargo
metadata resolution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

3 participants