keyway

module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 7, 2026 License: Apache-2.0

README ΒΆ

πŸ”‘ Keyway

Know which consumers will break before you rotate a signing key, change an issuer, or drop a claim.

CI CodeQL OpenSSF Scorecard Go Reference Go Report Card License


Keyway derives your JWT consumer inventory automatically β€” from Istio, Envoy, Kubernetes, OIDC discovery and a shipped library-behaviour database β€” and verifies it with real tokens against real staging endpoints using 13 purpose-built probes. It versions the derived contract, diffs it on every change, classifies each change as widened or narrowed, and answers the one question that actually matters before a key rotation:

"If I rotate this key, who breaks, and how long is the safe grace period?"

Keyway never mutates your configuration, never blocks a deploy, and never asks you to author a model file. If a feature needs you to describe your own system, it is out of scope by definition.

Why

Key rotations, issuer migrations and claim removals cause outages because nobody has an accurate, current map of who validates what. That map is normally tribal knowledge that rots. Keyway rebuilds it on every run and, crucially, proves it by minting synthetic tokens (expired, wrong-issuer, alg=none, tampered, canary, …) and watching how each consumer responds.

What it does

Capability How
Auto-discovers consumers Istio RequestAuthentication, Envoy jwt_authn, K8s projected SA tokens, OIDC/Keycloak client registry
Verifies with real tokens 13 probes (valid, expired, wrong-issuer/audience, alg=none, alg-confusion, tampered, missing-claim, retired-key, canary, header-bypass, …), staging-only, with a hard production guard
Versions & diffs the contract Canonical SHA-256 hash; identical systems produce identical hashes; a first run establishes a baseline with zero alerts
Classifies changes widened / narrowed / neutral / unknown, with severity
Answers blast radius keyway blast-radius rotate-key --issuer … --kid … β†’ who breaks, who's ready, recommended grace period, bounding consumer
Runs a canary key Announces a key in JWKS without signing, then measures which consumers pick it up
Web dashboard React + TypeScript UI over the HTTP API

Quickstart

Try it β€” zero config. Runs the app and the embedded web UI on an in-memory store; no database, no cluster required.

make demo                                  # build + run on http://localhost:8080
# or, prebuilt (published on each release):
docker run -p 8080:8080 ghcr.io/keyway-ai/keyway

Open http://localhost:8080. The UI loads on built-in sample data, so you can explore findings, coverage, blast radius and the agent inspector right away. (To drive the live API instead of sample data, set KEYWAY_API_TOKEN and connect from the UI's Settings.)

Run it for real. Point Keyway at your own configs and a Postgres store:

# 1. Bring up Postgres (+ a reference Keycloak) for local dev, and point Keyway at it
make dev-up
export KEYWAY_DB_URL=postgres://keyway:keyway@localhost:5432/keyway?sslmode=disable

# 2. Build the binaries
make build

# 3. Register your issuers
./bin/keyway init
./bin/keyway issuer add --type keycloak --url https://kc.example.com/realms/main \
    --admin-credential-env KC_ADMIN

# 4. Discover consumers and snapshot the contract (first run = baseline, zero alerts)
./bin/keyway discover --namespace default
./bin/keyway snapshot

# 5. Model a rotation
./bin/keyway blast-radius rotate-key --issuer keycloak-main --kid rsa-2026-01

Example output (issuer/kid are placeholders):

Rotating rsa-2026-01 on keycloak-main affects 47 consumers.

WILL BREAK (3)
  payments-api          48h JWKS cache, RefreshUnknownKID=false   [probe:canary_key #8812]
                        owner: team-payments
  legacy-reporting      no JWKS refresh configured                [lib:keyfunc v1.9.0]
                        owner: team-data
  mobile-gateway        cached key pinned in config               [istio:RequestAuthentication/mobile-gw]
                        owner: team-mobile

READY (41)   run with --verbose to list
UNKNOWN (3)  insufficient evidence β€” not probeable

RECOMMENDED GRACE PERIOD: 9d 6h
  bound by payments-api (48h cache, measured 6d4h to pick up canary, x1.5 margin)
  NOTE: 3 consumers unknown β€” treat as a lower bound.

Web UI

The single binary serves the UI at /. For frontend work, the Vite dev server runs standalone on sample data β€” no backend needed:

make demo         # the app + embedded UI on :8080 (in-memory store)
make serve        # the API + scheduler on :8080 (uses your KEYWAY_DB_URL)
make web-dev      # Vite dev server on :5173, sample data + hot reload (proxies /v1 β†’ :8080)

In CI

Gate pull requests on token-contract drift with the CLI or the GitHub Action β€” against Keyway Cloud (hosted or your own keyway-cloud) for shared history, or fully offline against a committed baseline (no account, no network):

- uses: Keyway-AI/keyway@v0
  with:
    server: https://cloud.example.com   # omit for offline mode
    token: ${{ secrets.KEYWAY_TOKEN }}
    project: ${{ vars.KEYWAY_PROJECT }}
    path: deploy/
    fail-on: high
keyway cloud analyze --path deploy/ --baseline .keyway/baseline.json --fail-on high

See docs/ci.md for both modes, all flags, and token setup.

Architecture

 discovery ──┐
             β”œβ”€β–Ά contract build ─▢ hash/version ─▢ diff ─▢ classify ─▢ notify
 issuers  ────          β”‚                                     β–²
             β”‚          β–Ό                                     β”‚
 libdefaults β”‚       probe engine (13 probes) β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚          β”‚
             └──────────┴─▢ blast radius + grace period ─▢ CLI / HTTP API / Web UI

See ARCHITECTURE.md for the full guide β€” the layering, the data flow, the extension seams, and where to change things β€” and docs/architecture-review.md for the independent design critique.

How accurate is it?

On a corpus of 805 realistic before/after changes (half real contract changes, half ordinary redeploy noise), Keyway catches 100% of real changes with 0% false alarms β€” including a "noisy redeploy" that churns six unrelated things at once. See BENCHMARK.md for the plain-English study and market comparison, docs/benchmark.md for methodology, and reproduce it yourself:

make bench            # scorecard
make bench-report     # + an interactive report.html

And it's validated against real, documented incidents β€” alg=none (CVE-2022-23540), RS256β†’HS256 confusion (CVE-2022-23541), and the JWKS key-rotation outage from openfga/openfga#3099. Keyway detects 8 of 8 β€” see docs/realworld-validation.md:

make validate         # reproduce each cited incident and check Keyway flags it

And the live-probe layer is scored end-to-end against real containerized services β€” one secure, one per weakness (alg=none, RS256β†’HS256 confusion, unverified signature, missing aud/iss/exp, header trust). Keyway returns the correct verdict on 8/8 (100%):

make bench-l2         # docker-compose rig + Keyway's real probe engine (bench/l2)

CI fails the build if accuracy drops below the PRD Β§13.4 thresholds or if Keyway stops detecting any documented real-world risk.

Project status

Keyway is actively developed. Milestones follow Β§15 of the PRD (M1–M9); the build tracker lives in docs/progress.md and the open-items register in docs/known-issues.md.

Contributing & community

Contributions are welcome β€” start with CONTRIBUTING.md. Keyway ships a 400-scenario benchmark harness (bench/) that gates accuracy in CI, so new discovery/probe/diff logic is expected to keep the scorecard above the Β§13.4 thresholds.

Security

Keyway mints synthetic tokens and talks to auth infrastructure. Please read SECURITY.md before running it, and never point it at production without the explicit --i-know-this-is-production flag. Report vulnerabilities per SECURITY.md.

We hold Keyway to a documented secure-development process β€” SAST (CodeQL, gosec), DAST (OWASP ZAP against the demo), dependency + secret scanning (govulncheck, gitleaks, Trivy, Dependabot), SBOMs and signed release images. See docs/security/ssdlc.md.

License

Apache License 2.0.

Directories ΒΆ

Path Synopsis
bench
harness command
Command harness runs the Keyway accuracy benchmark corpus and emits a scorecard (PRD Β§13).
Command harness runs the Keyway accuracy benchmark corpus and emits a scorecard (PRD Β§13).
l2 command
Command l2 is the live-probe (L2) benchmark rig.
Command l2 is the live-probe (L2) benchmark rig.
mutations
Package mutations generates benchmark scenarios: known contract changes (true positives) and no-op changes (false positives).
Package mutations generates benchmark scenarios: known contract changes (true positives) and no-op changes (false positives).
realworld
Package realworld validates Keyway against documented, real-world JWT/JWKS incidents (CVEs, GitHub issues, postmortems).
Package realworld validates Keyway against documented, real-world JWT/JWKS incidents (CVEs, GitHub issues, postmortems).
Package cloud is the multi-tenant hosted layer ("Keyway Cloud") on top of the open-source engine.
Package cloud is the multi-tenant hosted layer ("Keyway Cloud") on top of the open-source engine.
cmd
keyway command
Command keyway is the Keyway CLI.
Command keyway is the Keyway CLI.
keyway-cloud command
Command keyway-cloud is the multi-tenant hosted API ("Keyway Cloud"): accounts, projects, and persisted auth-contract analysis over the shared engine.
Command keyway-cloud is the multi-tenant hosted API ("Keyway Cloud"): accounts, projects, and persisted auth-contract analysis over the shared engine.
keyway-runner command
Command keyway-runner is the Keyway daemon: the same functionality as the CLI but defaulting to `serve` (API + scheduler) for in-VPC deployment.
Command keyway-runner is the Keyway daemon: the same functionality as the CLI but defaulting to `serve` (API + scheduler) for in-VPC deployment.
internal
agentauth
Package agentauth statically analyzes an agent / MCP / on-behalf-of token against the agent-auth invariants in the threat taxonomy (internal/threats, "agent" domain).
Package agentauth statically analyzes an agent / MCP / on-behalf-of token against the agent-auth invariants in the threat taxonomy (internal/threats, "agent" domain).
api
Package api serves the Keyway HTTP API (PRD Β§12) and the embedded web dashboard.
Package api serves the Keyway HTTP API (PRD Β§12) and the embedded web dashboard.
app
Package app is Keyway's application (use-case) layer.
Package app is Keyway's application (use-case) layer.
attack
Package attack is Keyway's generative, invariant-based JWT attack harness.
Package attack is Keyway's generative, invariant-based JWT attack harness.
attribution
Package attribution binds contract changes to their cause: a git commit/PR, a Kubernetes deploy, or a Keycloak admin event (PRD Β§16 OPEN-5).
Package attribution binds contract changes to their cause: a git commit/PR, a Kubernetes deploy, or a Keycloak admin event (PRD Β§16 OPEN-5).
blastradius
Package blastradius answers "if I make this change, who breaks?" and derives a safe grace period (PRD Β§10).
Package blastradius answers "if I make this change, who breaks?" and derives a safe grace period (PRD Β§10).
cli
Package cli builds the Keyway command tree, shared by the `keyway` CLI and the `keyway-runner` daemon binaries.
Package cli builds the Keyway command tree, shared by the `keyway` CLI and the `keyway-runner` daemon binaries.
config
Package config loads and validates Keyway runtime configuration from a YAML file and/or environment variables.
Package config loads and validates Keyway runtime configuration from a YAML file and/or environment variables.
contract
Package contract assembles the derived contract graph, hashes it canonically, and manages versioning / the baseline flow.
Package contract assembles the derived contract graph, hashes it canonically, and manages versioning / the baseline flow.
coordination
Package coordination provides the cross-process seams a multi-replica Keyway deployment needs: a shared idempotency store (so a retried write replays the same result on any replica) and a leader gate (so exactly one replica runs the scheduler).
Package coordination provides the cross-process seams a multi-replica Keyway deployment needs: a shared idempotency store (so a retried write replays the same result on any replica) and a leader gate (so exactly one replica runs the scheduler).
diff
Package diff compares two contract versions and classifies each field change.
Package diff compares two contract versions and classifies each field change.
discovery
Package discovery derives the consumer inventory automatically from cluster and config sources.
Package discovery derives the consumer inventory automatically from cluster and config sources.
discovery/envoy
Package envoy discovers consumers from Envoy jwt_authn provider configuration.
Package envoy discovers consumers from Envoy jwt_authn provider configuration.
discovery/istio
Package istio discovers consumers from Istio RequestAuthentication resources (confidence 1.0 β€” declarative and unambiguous).
Package istio discovers consumers from Istio RequestAuthentication resources (confidence 1.0 β€” declarative and unambiguous).
discovery/k8s
Package k8s discovers consumers from Kubernetes Services, their backing workloads, and projected service-account token volumes (PRD Β§7.3).
Package k8s discovers consumers from Kubernetes Services, their backing workloads, and projected service-account token volumes (PRD Β§7.3).
discovery/kube
Package kube builds Kubernetes API clients for the in-cluster discovery path.
Package kube builds Kubernetes API clients for the in-cluster discovery path.
discovery/oidcclient
Package oidcclient discovers consumers from an OIDC provider's client registry (Keycloak).
Package oidcclient discovers consumers from an OIDC provider's client registry (Keycloak).
issuer
Package issuer defines the issuer adapter interface and provides per-issuer-type implementations (keycloak, k8ssa, generic).
Package issuer defines the issuer adapter interface and provides per-issuer-type implementations (keycloak, k8ssa, generic).
issuer/generic
Package generic implements issuer.Adapter for a generic OIDC issuer whose signing keys Keyway controls locally.
Package generic implements issuer.Adapter for a generic OIDC issuer whose signing keys Keyway controls locally.
issuer/k8ssa
Package k8ssa implements issuer.Adapter for Kubernetes service-account token issuers.
Package k8ssa implements issuer.Adapter for Kubernetes service-account token issuers.
issuer/keycloak
Package keycloak implements issuer.Adapter for Keycloak realms.
Package keycloak implements issuer.Adapter for Keycloak realms.
issuer/localkeys
Package localkeys manages a set of locally-controlled signing keys and the JOSE operations Keyway performs with them: minting tokens, publishing a JWKS, and running the announce β†’ active β†’ retired key lifecycle used by the canary flow (PRD Β§4.1, Β§6, Β§10).
Package localkeys manages a set of locally-controlled signing keys and the JOSE operations Keyway performs with them: minting tokens, publishing a JWKS, and running the announce β†’ active β†’ retired key lifecycle used by the canary flow (PRD Β§4.1, Β§6, Β§10).
issuer/oidc
Package oidc provides OIDC discovery and JWKS fetching over HTTP, shared by issuer adapters that read an issuer's published metadata.
Package oidc provides OIDC discovery and JWKS fetching over HTTP, shared by issuer adapters that read an issuer's published metadata.
issuerregistry
Package issuerregistry builds and holds live issuer adapters, the runtime home of Keyway-operated signing keys and canary state.
Package issuerregistry builds and holds live issuer adapters, the runtime home of Keyway-operated signing keys and canary state.
keystore
Package keystore persists Keyway-operated signing keys so canary state survives a daemon restart (KI-09).
Package keystore persists Keyway-operated signing keys so canary state survives a daemon restart (KI-09).
libdefaults
Package libdefaults ships Keyway's library-behavior database and looks up known JWKS behavior by library name and version.
Package libdefaults ships Keyway's library-behavior database and looks up known JWKS behavior by library name and version.
model
Package model holds Keyway's core domain types.
Package model holds Keyway's core domain types.
notify
Package notify delivers change events to external sinks (Slack, webhooks).
Package notify delivers change events to external sinks (Slack, webhooks).
ports
Package ports holds the small shared interfaces (hexagonal "ports") that would otherwise be duplicated across domain packages only to dodge an import cycle.
Package ports holds the small shared interfaces (hexagonal "ports") that would otherwise be duplicated across domain packages only to dodge an import cycle.
probe
Package probe mints synthetic tokens and verifies consumer behavior against real staging endpoints (PRD Β§6).
Package probe mints synthetic tokens and verifies consumer behavior against real staging endpoints (PRD Β§6).
store
Package store defines the persistence interface for Keyway and provides implementations (see store/postgres).
Package store defines the persistence interface for Keyway and provides implementations (see store/postgres).
store/memory
Package memory is an in-memory store.Store implementation.
Package memory is an in-memory store.Store implementation.
store/open
Package open is the store composition helper: it turns a DSN into a store.Store, so callers depend on the persistence interface rather than a concrete backend.
Package open is the store composition helper: it turns a DSN into a store.Store, so callers depend on the persistence interface rather than a concrete backend.
store/postgres
Package postgres implements store.Store on PostgreSQL via pgx (PRD Β§2).
Package postgres implements store.Store on PostgreSQL via pgx (PRD Β§2).
threats
Package threats is Keyway's JWT/JWKS/OIDC threat taxonomy β€” the *denominator* against which detection coverage is measured.
Package threats is Keyway's JWT/JWKS/OIDC threat taxonomy β€” the *denominator* against which detection coverage is measured.
version
Package version exposes build metadata injected at link time via -ldflags.
Package version exposes build metadata injected at link time via -ldflags.
pkg
apitypes
Package apitypes holds the public request/response shapes for the Keyway HTTP API (PRD Β§12).
Package apitypes holds the public request/response shapes for the Keyway HTTP API (PRD Β§12).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL