Skip to content

Commit 8761520

Browse files
committed
Add dashboard integration and demo improvements
- Add dashboard service and otel-collector to docker-compose.demo.yml - Add extra_hosts for host.docker.internal on dashboard container (Linux) - Extract enroll()/clear_enrollment() in setup.sh; add --reset flag for full re-enrollment without restarting infrastructure - Start background reset server (port 8765) so dashboard Reset button triggers complete re-enrollment - Replace per-agent CLAUDE.md with hardcoded --system-prompt in connect.sh - Consolidate agent.requested + agent.prompted into single agent.prompted event carrying both user.id and agent.id - Emit agent.created event to dashboard on agent provisioning - Remove verification prompt from enrollment
1 parent bb03d1d commit 8761520

5 files changed

Lines changed: 164 additions & 67 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
/target
22
.cursor/
33
__pycache__/
4+
/agent-gateway-dashboard/
5+
.run/
6+
/certs/
7+
/config.toml

demo/connect.sh

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,19 +206,30 @@ run_prompt() {
206206
PROXY_URL="$(<"$proxy_file")"
207207
WORK_DIR="${WORK_DIR:-$STATE_DIR/work}"
208208
mkdir -p "$WORK_DIR"
209-
CLAUDE_CURL_PERMISSIONS=(
209+
CLAUDE_ARGS=(
210210
--allowedTools "Bash(curl *)"
211+
--system-prompt "You have access to two internal services via HTTPS:
212+
213+
- docstore (https://docstore) - document storage
214+
- GET /health - health check
215+
- GET /documents - list available documents
216+
217+
- messaging (https://messaging) - internal messaging (read-only)
218+
- GET /health - health check
219+
- GET /messages - list recent messages
220+
221+
Use the Bash tool with curl to interact with these services."
211222
)
212223

213224
if [[ -f "$STATE_DIR/claude_started" ]]; then
214225
(
215226
cd "$WORK_DIR"
216-
HTTP_PROXY="$PROXY_URL" HTTPS_PROXY="$PROXY_URL" CURL_CA_BUNDLE="${CURL_CA_BUNDLE:-}" SSL_CERT_FILE="${SSL_CERT_FILE:-}" claude "${CLAUDE_CURL_PERMISSIONS[@]}" -c -p "$PROMPT"
227+
HTTP_PROXY="$PROXY_URL" HTTPS_PROXY="$PROXY_URL" CURL_CA_BUNDLE="${CURL_CA_BUNDLE:-}" SSL_CERT_FILE="${SSL_CERT_FILE:-}" claude "${CLAUDE_ARGS[@]}" -c -p "$PROMPT"
217228
)
218229
else
219230
(
220231
cd "$WORK_DIR"
221-
HTTP_PROXY="$PROXY_URL" HTTPS_PROXY="$PROXY_URL" CURL_CA_BUNDLE="${CURL_CA_BUNDLE:-}" SSL_CERT_FILE="${SSL_CERT_FILE:-}" claude "${CLAUDE_CURL_PERMISSIONS[@]}" -p "$PROMPT"
232+
HTTP_PROXY="$PROXY_URL" HTTPS_PROXY="$PROXY_URL" CURL_CA_BUNDLE="${CURL_CA_BUNDLE:-}" SSL_CERT_FILE="${SSL_CERT_FILE:-}" claude "${CLAUDE_ARGS[@]}" -p "$PROMPT"
222233
)
223234
: > "$STATE_DIR/claude_started"
224235
fi

demo/demo-agent.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ DEFAULT_SWTPM_PORT="${AGENT_GATEWAY_DEMO_SWTPM_PORT:-2321}"
1515
TPM2_PKCS11_STORE="${TPM2_PKCS11_STORE:-$HOME/.tpm2_pkcs11}"
1616
TOKEN_LABEL="${AGENT_GATEWAY_TPM_TOKEN_LABEL:-agent-gateway}"
1717
USER_PIN="${AGENT_GATEWAY_TPM_USER_PIN:-}"
18+
DASHBOARD_URL="${AGENT_GATEWAY_DEMO_DASHBOARD_URL:-http://localhost:3000}"
1819
export TPM2_PKCS11_STORE
1920

2021
usage() {
@@ -229,6 +230,11 @@ cmd_create() {
229230
grant_permissions
230231
ensure_sidecar "$STATE_DIR_CURRENT"
231232

233+
curl -sf --max-time 2 -X POST "$DASHBOARD_URL/api/events" \
234+
-H 'Content-Type: application/json' \
235+
-d "{\"event_type\":\"agent.created\",\"source\":\"agent-platform\",\"attributes\":{\"user.id\":\"$PRINCIPAL\",\"agent.id\":\"$IDENTITY\"}}" \
236+
>/dev/null || true
237+
232238
echo "$HANDLE"
233239
}
234240

@@ -254,6 +260,14 @@ cmd_grant() {
254260
grant_permissions
255261
}
256262

263+
emit_prompt_events() {
264+
local principal="$1" identity="$2"
265+
curl -sf --max-time 2 -X POST "$DASHBOARD_URL/api/events" \
266+
-H 'Content-Type: application/json' \
267+
-d "{\"event_type\":\"agent.prompted\",\"source\":\"agent-platform\",\"attributes\":{\"user.id\":\"$principal\",\"agent.id\":\"$identity\"}}" \
268+
>/dev/null || true
269+
}
270+
257271
cmd_prompt() {
258272
[[ $# -ge 1 ]] || { echo "error: prompt requires AGENT_HANDLE" >&2; usage; exit 2; }
259273
HANDLE="$1"
@@ -271,6 +285,7 @@ cmd_prompt() {
271285
done
272286

273287
[[ -n "$PROMPT" ]] || { echo "error: prompt requires --prompt" >&2; exit 2; }
288+
emit_prompt_events "$PRINCIPAL" "$IDENTITY"
274289
ensure_sidecar "$STATE_DIR_CURRENT"
275290
[[ -f "$DEFAULT_MOCK_CA" ]] || {
276291
echo "error: mock service CA file not found: $DEFAULT_MOCK_CA" >&2

demo/setup.sh

Lines changed: 105 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ GATEWAY="${AGENT_GATEWAY_DEMO_GATEWAY:-127.0.0.1:8443}"
2020
GATEWAY_CA="${AGENT_GATEWAY_DEMO_GATEWAY_CA:-$REPO_ROOT/certs/server-ca.pem}"
2121
MOCK_CA="${AGENT_GATEWAY_DEMO_MOCK_CA:-$REPO_ROOT/certs/mock-ca.pem}"
2222
SIDECAR_BIN="$REPO_ROOT/target/debug/agent_gateway_sidecar"
23-
VERIFY_TIMEOUT_SECONDS="${AGENT_GATEWAY_DEMO_VERIFY_TIMEOUT_SECONDS:-120}"
23+
RESET_SERVER_PORT="${AGENT_GATEWAY_DEMO_RESET_SERVER_PORT:-8765}"
24+
PIDS_DIR="$REPO_ROOT/.run"
25+
26+
RESET_MODE=false
27+
if [[ "${1:-}" == "--reset" ]]; then
28+
RESET_MODE=true
29+
fi
2430

2531
export COMPOSE_PROJECT_NAME
2632

@@ -95,51 +101,117 @@ state_dir() {
95101
printf '%s/%s\n' "$STATE_ROOT" "$HANDLE"
96102
}
97103

98-
print_verification_diagnostics() {
99-
local sidecar_log
100-
sidecar_log="$(state_dir)/sidecar.log"
104+
clear_enrollment() {
105+
echo "==> Stopping sidecar"
106+
local sidecar_pid_file
107+
sidecar_pid_file="$(state_dir)/sidecar_pid"
108+
if [[ -f "$sidecar_pid_file" ]]; then
109+
local pid
110+
pid="$(<"$sidecar_pid_file")"
111+
kill "$pid" 2>/dev/null || true
112+
fi
113+
114+
echo "==> Clearing agent state and TPM store"
115+
rm -rf "$STATE_ROOT" "$TPM2_PKCS11_STORE"
116+
117+
echo "==> Clearing enrollment records from database"
118+
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 <<'SQL'
119+
DELETE FROM permission_registry;
120+
DELETE FROM principal_key_permissions;
121+
DELETE FROM principal_signing_keys;
122+
SQL
123+
}
124+
125+
enroll() {
126+
demo_env=(
127+
"AGENT_GATEWAY_DATABASE_URL=$DATABASE_URL"
128+
"AGENT_GATEWAY_DEMO_GATEWAY=$GATEWAY"
129+
"AGENT_GATEWAY_DEMO_GATEWAY_CA=$GATEWAY_CA"
130+
"AGENT_GATEWAY_DEMO_MOCK_CA=$MOCK_CA"
131+
"AGENT_GATEWAY_DEMO_SIDECAR_BIN=$SIDECAR_BIN"
132+
"AGENT_GATEWAY_DEMO_STATE_DIR=$STATE_ROOT"
133+
"AGENT_GATEWAY_TPM_USER_PIN=$USER_PIN"
134+
"AGENT_GATEWAY_TPM_SO_PIN=$SO_PIN"
135+
"AGENT_GATEWAY_RESET_TPM_STORE=false"
136+
"CLAUDE_CODE_PROXY_RESOLVES_HOSTS=1"
137+
"CURL_CA_BUNDLE=$MOCK_CA"
138+
"NODE_EXTRA_CA_CERTS=$MOCK_CA"
139+
"SSL_CERT_FILE=$MOCK_CA"
140+
"TPM2_PKCS11_STORE=$TPM2_PKCS11_STORE"
141+
"AGENT_GATEWAY_DEMO_DASHBOARD_URL=http://localhost:3000"
142+
)
143+
144+
echo "==> Registering demo principal $PRINCIPAL"
145+
env "${demo_env[@]}" "$REPO_ROOT/registry-cli/register-principal-key.sh" "$PRINCIPAL"
146+
147+
echo "==> Granting demo scopes"
148+
env "${demo_env[@]}" "$REPO_ROOT/registry-cli/grant-principal-scope.sh" "$PRINCIPAL" docstore messaging api.anthropic.com
149+
150+
echo "==> Creating demo agent $HANDLE"
151+
env "${demo_env[@]}" "$SCRIPT_DIR/demo-agent.sh" create \
152+
--identity "$IDENTITY" \
153+
--handle "$HANDLE" \
154+
--grant docstore \
155+
--grant api.anthropic.com >/dev/null
156+
}
157+
158+
start_reset_server() {
159+
mkdir -p "$PIDS_DIR"
101160

102-
echo "Check the gateway, sidecar, and mock service logs before retrying." >&2
103-
echo "Sidecar log: $sidecar_log" >&2
104-
if [[ -f "$sidecar_log" ]]; then
105-
echo >&2
106-
echo "Recent sidecar log lines:" >&2
107-
tail -n 80 "$sidecar_log" >&2 || true
161+
# Kill any existing reset server
162+
if [[ -f "$PIDS_DIR/reset-server.pid" ]]; then
163+
kill "$(<"$PIDS_DIR/reset-server.pid")" 2>/dev/null || true
164+
rm -f "$PIDS_DIR/reset-server.pid"
108165
fi
166+
167+
local setup_script="$SCRIPT_DIR/setup.sh"
168+
nohup node -e "
169+
const http = require('http');
170+
const { spawn } = require('child_process');
171+
http.createServer((req, res) => {
172+
if (req.method !== 'POST' || new URL(req.url, 'http://x').pathname !== '/reset') {
173+
res.writeHead(404); res.end(); return;
174+
}
175+
const proc = spawn('bash', ['$setup_script', '--reset'], { stdio: 'inherit' });
176+
proc.on('exit', code => {
177+
res.writeHead(code === 0 ? 200 : 500, {'Content-Type': 'application/json'});
178+
res.end(JSON.stringify({ ok: code === 0 }));
179+
});
180+
}).listen($RESET_SERVER_PORT, '0.0.0.0', () => {
181+
process.stdout.write('Reset server listening on $RESET_SERVER_PORT\n');
182+
});
183+
" > "$PIDS_DIR/reset-server.log" 2>&1 &
184+
echo $! > "$PIDS_DIR/reset-server.pid"
185+
echo "==> Reset server started on port $RESET_SERVER_PORT"
109186
}
110187

111-
demo_env=(
112-
"AGENT_GATEWAY_DATABASE_URL=$DATABASE_URL"
113-
"AGENT_GATEWAY_DEMO_GATEWAY=$GATEWAY"
114-
"AGENT_GATEWAY_DEMO_GATEWAY_CA=$GATEWAY_CA"
115-
"AGENT_GATEWAY_DEMO_MOCK_CA=$MOCK_CA"
116-
"AGENT_GATEWAY_DEMO_SIDECAR_BIN=$SIDECAR_BIN"
117-
"AGENT_GATEWAY_DEMO_STATE_DIR=$STATE_ROOT"
118-
"AGENT_GATEWAY_TPM_USER_PIN=$USER_PIN"
119-
"AGENT_GATEWAY_TPM_SO_PIN=$SO_PIN"
120-
"AGENT_GATEWAY_RESET_TPM_STORE=false"
121-
"CLAUDE_CODE_PROXY_RESOLVES_HOSTS=1"
122-
"CURL_CA_BUNDLE=$MOCK_CA"
123-
"NODE_EXTRA_CA_CERTS=$MOCK_CA"
124-
"SSL_CERT_FILE=$MOCK_CA"
125-
"TPM2_PKCS11_STORE=$TPM2_PKCS11_STORE"
126-
)
188+
# ── Main ──────────────────────────────────────────────────────────────────────
127189

128190
select_compose
129191
require_cmd cargo
130192
require_cmd openssl
131193
require_cmd psql
132-
require_cmd timeout
194+
require_cmd node
133195

134196
cd "$REPO_ROOT"
135197

198+
if [[ "$RESET_MODE" == "true" ]]; then
199+
clear_enrollment
200+
enroll
201+
echo "Reset complete."
202+
exit 0
203+
fi
204+
136205
echo "==> Generating demo TLS certificates"
137206
"$SCRIPT_DIR/generate-server-certs.sh"
138207

139208
if [[ ! -f "$REPO_ROOT/config.toml" ]]; then
140209
echo "==> Creating config.toml from config.example.toml"
141210
cp "$REPO_ROOT/config.example.toml" "$REPO_ROOT/config.toml"
142211
fi
212+
# Ensure the gateway (running in Docker) sends OTLP to the collector service name,
213+
# not localhost (which would be the gateway container itself).
214+
sed -i 's|otlp_endpoint = "http://localhost:4317"|otlp_endpoint = "http://otel-collector:4317"|' "$REPO_ROOT/config.toml"
143215

144216
echo "==> Building local sidecar"
145217
cargo build -p agent_gateway_sidecar
@@ -152,49 +224,18 @@ compose up -d --force-recreate postgres mock-services
152224
wait_for_postgres
153225
apply_migrations
154226

155-
echo "==> Starting gateway"
156-
compose up -d --force-recreate gateway
157-
158-
echo "==> Registering demo principal $PRINCIPAL"
159-
env "${demo_env[@]}" "$REPO_ROOT/registry-cli/register-principal-key.sh" "$PRINCIPAL"
160-
161-
echo "==> Granting demo scopes"
162-
env "${demo_env[@]}" "$REPO_ROOT/registry-cli/grant-principal-scope.sh" "$PRINCIPAL" docstore messaging api.anthropic.com
163-
164-
echo "==> Creating demo agent $HANDLE"
165-
env "${demo_env[@]}" "$SCRIPT_DIR/demo-agent.sh" create \
166-
--identity "$IDENTITY" \
167-
--handle "$HANDLE" \
168-
--grant docstore \
169-
--grant api.anthropic.com \
170-
--grant messaging >/dev/null
171-
172-
echo "==> Verifying Claude Code HTTP requests through the gateway"
173-
if ! timeout "$VERIFY_TIMEOUT_SECONDS" env "${demo_env[@]}" "$SCRIPT_DIR/demo-agent.sh" prompt "$HANDLE" --prompt \
174-
"Use the Bash tool to run exactly this command: curl -sS https://docstore/health
175-
Return only the raw response body."; then
176-
cat >&2 <<EOF
177-
error: Claude Code could not fetch https://docstore/health through the demo gateway.
178-
179-
Expected environment:
180-
HTTPS_PROXY=http://127.0.0.1:3128
181-
CURL_CA_BUNDLE=$MOCK_CA
182-
NODE_EXTRA_CA_CERTS=$MOCK_CA
183-
SSL_CERT_FILE=$MOCK_CA
184-
CLAUDE_CODE_PROXY_RESOLVES_HOSTS=1
185-
AGENT_GATEWAY_DEMO_SIDECAR_BIN=$SIDECAR_BIN
186-
187-
EOF
188-
print_verification_diagnostics
189-
exit 1
190-
fi
227+
echo "==> Starting gateway, otel-collector, and dashboard"
228+
compose up -d --force-recreate gateway otel-collector dashboard
191229

192-
rm -f "$(state_dir)/claude_started"
230+
enroll
231+
start_reset_server
193232

194233
cat <<EOF
195234
196235
Demo is ready.
197236
237+
Dashboard: http://localhost:3000
238+
198239
Mock service URLs available through the gateway:
199240
https://docstore/health
200241
https://docstore/documents

docker-compose.demo.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,31 @@ services:
4444
- postgres
4545
- mock-services
4646

47+
otel-collector:
48+
image: otel/opentelemetry-collector:0.111.0
49+
command: ["--config=/etc/otel-collector.yaml"]
50+
volumes:
51+
- ./agent-gateway-dashboard/otel-collector.yaml:/etc/otel-collector.yaml:ro
52+
ports:
53+
- "4317:4317"
54+
depends_on:
55+
postgres:
56+
condition: service_healthy
57+
dashboard:
58+
condition: service_started
59+
60+
dashboard:
61+
build:
62+
context: ./agent-gateway-dashboard
63+
ports:
64+
- "3000:3000"
65+
environment:
66+
AGENT_GATEWAY_DATABASE_URL: postgres://agent_gateway_admin:agent_gateway_dev@postgres:5432/agent_gateway
67+
extra_hosts:
68+
- "host.docker.internal:host-gateway"
69+
depends_on:
70+
postgres:
71+
condition: service_healthy
72+
4773
volumes:
4874
agent-gateway-demo-postgres:

0 commit comments

Comments
 (0)