Skip to content

Commit 3cfce4f

Browse files
hyperpolymathclaude
andcommitted
feat(vault-broker): add HTTP grant broker for LLM agent credential brokering
New standalone Rust crate: vault-broker. Exposes a minimal axum HTTP API on :9100 (internal Fly.io only): GET /health — liveness + credential count POST /v1/grants — issue one-use opaque grant for a hint POST /v1/grants/:id/redeem — redeem grant; get credential value Credentials loaded at startup from RGTV_CRED_<HINT> env vars, stored as Zeroizing<String>. Grants are UUID v4, configurable TTL (default 30s), one-use (dropped from memory immediately on redemption). Agent auth via Authorization: Bearer <RGTV_AGENT_TOKEN>. First consumer: proven-nesy-solver-api wires NESY_INGEST_TOKEN through this broker so agents never see the raw value. Also includes: - Containerfile: multi-stage Rust/Debian build, non-root user, curl HEALTHCHECK - fly.toml: rgtv-nesy app, internal-only TCP on :9100, HTTP health check, auto_stop/auto_start for on-demand operation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 784770b commit 3cfce4f

4 files changed

Lines changed: 495 additions & 0 deletions

File tree

vault-broker/Cargo.toml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
[package]
3+
name = "vault-broker"
4+
version = "0.1.0"
5+
edition = "2021"
6+
license = "PMPL-1.0-or-later"
7+
description = "RGTV HTTP grant broker — issues one-use opaque grants for LLM agents"
8+
authors = ["Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>"]
9+
repository = "https://github.com/hyperpolymath/reasonably-good-token-vault"
10+
11+
[[bin]]
12+
name = "vault-broker"
13+
path = "src/main.rs"
14+
15+
[dependencies]
16+
axum = { version = "0.7", features = ["macros"] }
17+
tokio = { version = "1", features = ["full"] }
18+
tower = "0.4"
19+
tower-http = { version = "0.5", features = ["trace"] }
20+
serde = { version = "1.0", features = ["derive"] }
21+
serde_json = "1.0"
22+
uuid = { version = "1.6", features = ["v4"] }
23+
zeroize = { version = "1.7", features = ["derive"] }
24+
chrono = { version = "0.4", features = ["serde"] }
25+
tracing = "0.1"
26+
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
27+
thiserror = "1.0"
28+
29+
[profile.release]
30+
lto = true
31+
codegen-units = 1
32+
panic = "abort"
33+
strip = true
34+
opt-level = 3

vault-broker/Containerfile

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# vault-broker Containerfile — multi-stage Rust build on Chainguard base
3+
4+
FROM docker.io/library/rust:1.85-slim-bookworm AS builder
5+
6+
RUN apt-get update && apt-get install -y --no-install-recommends \
7+
build-essential \
8+
pkg-config \
9+
libssl-dev \
10+
&& rm -rf /var/lib/apt/lists/*
11+
12+
WORKDIR /build
13+
COPY Cargo.toml Cargo.lock* ./
14+
COPY src ./src
15+
16+
RUN cargo build --release --bin vault-broker
17+
18+
# ---- Runtime ----
19+
FROM docker.io/library/debian:bookworm-slim
20+
21+
RUN apt-get update && apt-get install -y --no-install-recommends \
22+
ca-certificates \
23+
libssl3 \
24+
curl \
25+
&& rm -rf /var/lib/apt/lists/*
26+
27+
WORKDIR /app
28+
COPY --from=builder /build/target/release/vault-broker /app/vault-broker
29+
30+
RUN useradd -r -s /bin/false rgtv
31+
USER rgtv
32+
33+
EXPOSE 9100
34+
35+
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
36+
CMD curl -fs http://localhost:9100/health || exit 1
37+
38+
ENTRYPOINT ["/app/vault-broker"]

vault-broker/fly.toml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# Fly.io deployment for the RGTV vault-broker.
3+
#
4+
# Internal-only: the broker is reached via rgtv-nesy.flycast:9100 from
5+
# other apps in the same org (nesy-solver-api, etc.). No public edge.
6+
# Use .flycast (not .internal) so Fly auto-wakes the machine on first request.
7+
#
8+
# Required fly secrets (set BEFORE first deploy):
9+
# flyctl secrets set \
10+
# RGTV_AGENT_TOKEN=<shared-secret-also-set-on-nesy-solver-api> \
11+
# RGTV_CRED_NESY_INGEST_TOKEN=<value> \
12+
# -a rgtv-nesy
13+
#
14+
# Deploy:
15+
# cd vault-broker
16+
# flyctl launch --no-deploy --copy-config --name rgtv-nesy
17+
# flyctl secrets set RGTV_AGENT_TOKEN=... RGTV_CRED_NESY_INGEST_TOKEN=...
18+
# flyctl deploy -c fly.toml -f Containerfile
19+
20+
app = "rgtv-nesy"
21+
primary_region = "lhr"
22+
kill_signal = "SIGTERM"
23+
kill_timeout = "5s"
24+
25+
[build]
26+
dockerfile = "Containerfile"
27+
28+
[env]
29+
RUST_LOG = "vault_broker=info"
30+
RGTV_PORT = "9100"
31+
RGTV_GRANT_TTL_SECS = "30"
32+
# RGTV_AGENT_TOKEN and RGTV_CRED_* set via flyctl secrets
33+
34+
# Internal-only TCP on :9100 — no public http_service block.
35+
[[services]]
36+
protocol = "tcp"
37+
internal_port = 9100
38+
auto_stop_machines = "stop"
39+
auto_start_machines = true
40+
min_machines_running = 0
41+
processes = ["app"]
42+
43+
[[services.ports]]
44+
port = 9100
45+
handlers = [] # no TLS — 6PN internal only
46+
47+
[[services.http_checks]]
48+
interval = "30s"
49+
timeout = "5s"
50+
grace_period = "15s"
51+
method = "GET"
52+
path = "/health"
53+
54+
[[vm]]
55+
size = "shared-cpu-1x"
56+
memory = "256mb"
57+
cpu_kind = "shared"
58+
cpus = 1

0 commit comments

Comments
 (0)