Skip to content

feat: lookup(query), credential_for, dns_chain, secret_describe (WI-848, WI-864) - #50

Merged
thedancingdeveloper merged 1 commit into
mainfrom
feat/lookup-describe
Oct 4, 2026
Merged

thedancingdeveloper merged 1 commit into
mainfrom
feat/lookup-describe

Conversation

@thedancingdeveloper

Copy link
Copy Markdown
Contributor

Why

Agents bypassed Cadastre because it could not answer the questions they had:

  • 27/27 logged lookup errors were callers passing query (a phrase) where the required entity_id was expected.
  • context_for / check required fields were unclear from the schema.
  • Infisical project UUIDs were not discoverable — lookup returned infisical://apps/prod/NAME only.
  • Prod deploy build(deps): Bump the ui-minor group across 1 directory with 8 updates #49 failed on a secret with raw newlines that only a hand-written, value-reading script could detect.
  • The 2026-08-26 outage (proxied DNS pointed at the workload node, not the Caddy edge) was visible in catalog evidence nobody joined.

What

WI-848

  • lookup(entity_id | query, kind, limit): query ranks declared + observed entities and secrets by token overlap (declared and observed secrets merged by ref). Secret hits carry store, project_id, project_slug, environment, path, server and a ready agent-auth manifest line (VAR PROJECT_ID SECRET_NAME). CLI cadastre search, HTTP GET /search.
  • credential_for(service, action): ranked secrets from name matches + declared consumes_secret, action hints (deploy/ssh/push/...), revoked/retired names ranked last, plus the service's endpoints. CLI credential-for, HTTP GET /credential-for.
  • dns_chain(hostname): A/AAAA/CNAME (+ covering wildcard) → address owner (endpoint addresses, host x-*.addresses e.g. x-tailscale) → expected ingress (fronted_by) → node → service. Findings: points_at_workload, wrong_ingress, points_elsewhere, upstream_mismatch, unknown_address, no_record, declared_observed_differ. Catalog-only (no live resolution; LAN split-horizon stated as a limitation). The 08-26 misconfiguration is a fixture test.
  • Schemas: every MCP argument has a description; malformed calls get required/accepted args, a did-you-mean (context_for(query=…) → intent) and an example. Streamable HTTP now actually enforces context_for's intent (it used required_arguments or (), so a missing intent slipped through).

WI-864

  • secret_describe(ref) (ref, id, or bare key): project slug + id, env, path, server, version, updated_at/created_at, last rotated, consumers (declared consumes_secret + observed stacks interpolating the name), and value shape: length, bytes, lines, newline/CR/tab/control counts, whitespace flags, parses-as-JSON + JSON type, plus plain-language problems. CLI secret-describe, HTTP GET /secret-describe.
  • Design choice: the value never enters the serving process. The secrets-infisical collector already receives values from /api/v3/secrets/raw and drops them; it now reduces each value to core.secretshape counts in the same step and emits an x-secret-store block (declared in the plugin's attribute schema). This keeps DESIGN §8 ("secret values never transit the query layer") and needs no Infisical credential in the API/MCP containers (which don't hold one). Trade-off: shape freshness = last collect. No hash is stored (offline-guessing oracle).
  • orchestrator-gitops records interpolated variable names (${NAME}, $NAME, [[NAME]]) as x-orchestrator.variable_refs — names only.

Tests

  • tests/test_search_and_describe.py (23): shape function; collector emits shape/location with no value; komodo api key → manifest line without entity_id; declared/observed merge; config fallback for project id; lookup query/entity_id dispatch + error; did-you-mean/required/example errors; schema descriptions; credential ranking (pair first, revoked last); declared consumption; deploy-build(deps): Bump the ui-minor group across 1 directory with 8 updates #49 shape; consumers; full-ref JSON; Streamable HTTP + HTTP routes end to end. Every answer is rendered (JSON + text) and checked that no 5-char fragment of any fake secret value appears.
  • tests/test_dns_chain.py (10): the 2026-08-26 misconfiguration, VPN-evidence variant, correct chain, CNAME, wildcard, no record, unknown address, declared≠observed, limitations, normalization.
  • Tool/route inventories updated (stdio, Streamable, bridge, OpenAPI, parity — /search is named HTTP-only since MCP serves it as lookup(query=…)); ui/src/api/generated.ts regenerated.

Gates run locally: ruff check, ruff format --check, mypy, pytest -q (all pass), cadastre fmt --check, cadastre check compose.production.yaml --kind compose.

Deploy note

Not deployed. Live project_id/shape/consumers appear after the next cadastre collect with this version; until then secret hits fall back to the source's workspace_id when plugins.yaml is visible to the serving process, else report the project id as unknown.

🤖 Generated with Claude Code

Agents could not ask Cadastre the questions they actually had, so they
bypassed it: 27/27 logged `lookup` failures were a phrase passed where an
exact entity_id was required; the Infisical project id an API call needs was
not discoverable; and prod deploy #49 failed on a secret with raw newlines
that only a hand-written, value-reading script could find.

- lookup takes `entity_id` or `query`; a query ranks declared and observed
  entities and secrets (CLI `search`, HTTP /search). Secret hits carry
  store, project id/slug, environment, path, server and a ready agent-auth
  manifest line (WI-848).
- credential_for(service, action): ranked secrets from name matches and
  declared consumes_secret, retired names last (WI-848).
- dns_chain(hostname): record -> ingress edge -> node -> service from
  collected evidence; flags a proxied hostname pointing at a workload node
  instead of the edge (the 2026-08-26 outage, pinned by a fixture test).
  Catalog-only, never resolves live (WI-848).
- secret_describe(ref): location, version, timestamps, consumers and value
  shape (length, newline/CR counts, JSON parse) - never the value. The
  secrets-infisical collector computes the shape in its own process from
  the value its list call already returns (x-secret-store); the value never
  reaches the query layer. orchestrator-gitops records interpolated variable
  names so consumers can be named (WI-864).
- MCP schemas describe every argument; malformed calls get required and
  accepted arguments, a did-you-mean and an example. Streamable HTTP now
  enforces context_for's `intent` as required.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thedancingdeveloper
thedancingdeveloper enabled auto-merge (rebase) October 4, 2026 06:45
@thedancingdeveloper
thedancingdeveloper merged commit a5ad14d into main Oct 4, 2026
6 checks passed
@thedancingdeveloper
thedancingdeveloper deleted the feat/lookup-describe branch October 4, 2026 06:46
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