No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Sean OMeara 01c6bdeb9d
fix(binjovi-mcp): the OAuth service must know every MCP scope
Every authorize request naming binjovi.repository.create answered
{"error":"invalid_request"}, so create_repository could never be used
even with the scope on the client and the permission in the control
plane policy.

Binjovi.MCPOAuth.Protocol builds @scopes at COMPILE time from
Binjovi.Domain.MCPPermissions.scopes(), and refuses any request naming a
scope outside it. This Deployment was pinned to sha-833fc189660f, which
predates create_repository: that revision has no occurrence of the tool
at all. So the control plane advertised a tool whose scope this service
would never grant.

Measured, not guessed. The same authorize request was replayed against
the live endpoint twice: without the scope it answers 302, with it 400.

Bumped to sha-57d0ab2ab4be, the revision the control plane runs.

The two images are pinned in different repositories and nothing keeps
them in step; the comment says so, and says to bump this when a new MCP
tool ships.

Claude-Session: https://claude.ai/code/session_01JGe4pyhQ36AhgDfPJry4d3
2026-09-13 17:28:16 +02:00
apps feat: retire retained demo identity CRs for Binjovi ownership 2026-09-06 08:43:40 +02:00
docs fix(events): configure read-only client consent in Ory 2026-09-12 11:34:59 +02:00
manifests fix(binjovi-mcp): the OAuth service must know every MCP scope 2026-09-13 17:28:16 +02:00
tests fix(binjovi-mcp): grant the Claude client repository.create 2026-09-13 16:40:49 +02:00
BINJOVI.md docs: record native deploy cutover 2026-09-05 21:07:25 +02:00
BINJOVI_NATIVE_DELIVERY.md docs: record native Binjovi delivery 2026-09-05 17:50:40 +02:00
README.md docs: state native node-image route deployment order 2026-09-09 19:33:14 +02:00

sean/ory

Ory SSO manifests (Hydra / Kratos / Ory-UI / Oathkeeper / hydra-token-hook + the auth.sean.farm gateway route + the Vault/ESO credential plane), synced by the argocd control plane into ns ory. Migrated out of the Crossplane apis/oidc composition (the ory-aliceory singleton collapse).

  • apps/ — app-of-apps child Applications (ory-stack, ory-users), synced by the root ory-argocd Application (seeded from seanfarm kubernetes/flux/infrastructure/ory-argocd/).
  • manifests/stack/ — the Ory server stack (ns ory).
  • manifests/users/ — the 4 operator-managed Kratos identities: Alice, Bob, Sean, and Steve.

The Loki route accepts browser sessions and short-lived Hydra service JWTs. Loki does not use Oathkeeper identity headers, so this route uses the no-op mutator. The shared user-header mutator depends on Kratos browser fields and must not run for the service JWT path.

Hydra table maintenance (one-off, operator)

manifests/maintenance/job-hydra-vacuum-full.yaml rewrites the hydra_oauth2_access table and gives the disk back to the filesystem. Argo CD does not sync manifests/maintenance/. The two Applications in apps/ read manifests/stack and manifests/users only. Run this Job by hand.

The hourly janitor deletes expired tokens. A DELETE keeps the pages in the table for PostgreSQL to use again. It does not shrink the file. On 2026-09-07 the table held 541 MB and 160,151 live rows with 0 dead rows.

Read this before you run it. VACUUM FULL takes an ACCESS EXCLUSIVE lock. Hydra writes this table on every token grant. Every token grant waits until the rewrite ends. The Job sets lock_timeout to 10 seconds, so it fails fast instead of queueing behind the writers.

When to run it

Two conditions must be true first.

  1. The node agent roll must land. The DaemonSet still runs an old image. It asks for a new token on every call, about 14,000 tokens an hour. Those rows are live, not dead, so a rewrite gives back almost nothing. seanfarm pull request 400 rolls the agent to a release that keeps its token.
  2. The janitor must drain the backlog. It runs at minute 23 of every hour and removes at most 50,000 rows in one run.

The Job refuses to start while the live rows are above MAX_LIVE_ROWS (50,000). The precheck init container makes that test and exits non-zero.

Pick a quiet time. The CNPG scheduled backup runs every six hours, at 00:00, 06:00, 12:00 and 18:00 UTC. The janitor runs at minute 23. Stay away from those times.

Pre-checks (read-only)

Open a psql session on the cluster and run these. They change nothing.

kubectl -n cnpg exec -it postgres-1 -- psql -d hydra
SELECT pg_size_pretty(pg_total_relation_size('hydra_oauth2_access'));
SELECT n_live_tup, n_dead_tup, last_autovacuum
  FROM pg_stat_user_tables WHERE relname = 'hydra_oauth2_access';
SELECT count(*) FROM pg_stat_activity
  WHERE datname = 'hydra' AND pid <> pg_backend_pid() AND state <> 'idle';
SELECT count(*) FROM pg_locks
  WHERE NOT granted AND relation = 'hydra_oauth2_access'::regclass;

Go on only when n_live_tup is below 50,000. The PGDATA volume is 10Gi. The rewrite needs free space for a second copy of the table, so keep at least 1 GB free.

Run it

kubectl -n ory create -f manifests/maintenance/job-hydra-vacuum-full.yaml

Use create, not apply. The Job name carries a date. A later rewrite needs a new name.

Watch it

kubectl -n ory get job hydra-vacuum-full-20260907 -w
kubectl -n ory logs -f job/hydra-vacuum-full-20260907 -c precheck
kubectl -n ory logs -f job/hydra-vacuum-full-20260907 -c vacuum

Expect about 5 to 20 seconds when the live rows are below 30,000. Expect 1 to 3 minutes if the table is still large. The Job stops itself after 900 seconds. It never retries.

If the pod fails with a lock timeout, a writer held the table. Wait, then create the Job again under a new name.

The Job removes itself one day after it ends (ttlSecondsAfterFinished: 86400). Read the logs before then.

Binjovi release authentication

Binjovi is the only Ory build and release authority. Run binjovictl authority ory to verify that the provider-guarded handoff is authorized before you request a release.

The exact Binjovi release endpoints for bytey, binjovi, hello-lib, libseanfarm-operator, olly, ory, platform-seanfarm, seanfarm, and tetragon accept either a Kratos browser session or a Hydra bearer token. Oathkeeper verifies the bearer token and puts its subject and scope in a five-minute Binjovi identity token. Binjovi then permits only the binjovi-release-cli subject with the binjovi.release scope. Other Binjovi mutation routes do not accept this service identity, except the exact failed-staging-Deploy retry route, the exact Tetragon trunk-adoption route, and the exact Tetragon legacy-collaborator retirement route. The retirement route can remove only tetragon-pipeline-bot, after Binjovi verifies provider authority. The adoption route remains as an idempotent record of the completed shadow handoff. Binjovi rejects production retries and a second staging retry.

Laptop is read-only to Forgejo; push content with scripts/ory-push.sh <dir> (in-cluster pipeline-bot). That helper git push -fs the whole branch from a local directory, so it is last-writer-wins: diff a fresh clone against your tree before pushing, or you will silently revert someone else's work.

Binjovi health evidence

GET and HEAD requests below /health/ are anonymous. Deployment gates must read readiness and activity evidence without a browser session. The dashboard, assets, API, and all mutation routes remain authenticated.

The backend de-alias is DONE (2026-08-06), so the note that used to sit here — "DB names + Vault creds stay alice-named" — no longer applies. Postgres databases and roles are bare kratos / hydra; the OpenBaoAuth and OpenBaoDynamicSecret CRs carry no spec.namespace (root), with roles {kratos,hydra}-database and paths static-creds/{kratos,hydra}.

Kratos metadata handoff

Eve's identity CR was delivered with deletionPolicy: Retain in v0.1.35 and retired in v0.1.36. Alice, Bob, and Steve received Retain in v0.1.38. This change removes those three CRs so Argo can prune them while their remote identities and credentials remain. Password Secrets stay in the credential plane. Sean stays in platform bootstrap for the initial operator login.

Binjovi manages these four accounts' names, groups, and active states through kratos/seanfarm/identities/USERNAME. Each initial declaration binds the existing UUID, username, and email. It must verify the old CR is absent before it can adopt metadata ownership. Exact GET grants on those four CR names and the Kratos admin ingress grant stay in this repository so Binjovi can verify that no legacy writer returns.

Password rotation is not part of metadata ownership. Changing a retained password Secret no longer rotates the remote password after its CR is removed. Credentials, account creation, and UUID replacement require a separate path. Restore Kratos and Binjovi database backups together to keep UUID bindings.

Native pipeline timing

Binjovi uses native agents for this project's Build, Release, and Deploy tasks. The configured deployment target is ory-development. The deployment verifies the Ory Argo CD applications at the released Git revision.

Select an exact successful Build when you release:

binjovictl builds ory
binjovictl release ory --build BUILD_ID --wait
binjovictl status ory

Open the project dashboard and expand an execution attempt to inspect its task timeline and available profiles. The latest completed pipeline summary includes Build and Release time as well as deployment time. Check the Deploy result separately after Release completes. Telemetry explains execution; the stored revision and verification evidence establish completion.

The native node-image route accepts an agent JWT. Binjovi also checks the admission lane, current lease, and fence before it controls the isolated Job. The gateway does not grant a public node-image endpoint.

Deploy the node-image gateway route before you activate a project plan that uses node_image_job.v1. A gateway rejection must leave the Job unstarted.