Files
mesh-lab/scenarios/anthropic-bed.yml
T
jschoubben 71bea08f3b anthropic-bed: prove the host unseals the refresh token, no node-key stub
The bed follows the reworked flow: the manager module seals the refresh token to the node's
PUBLIC key, the HOST unseals it and mounts the cleartext at the manager's bound path, and the
refresh reads that cleartext -- no fake node key pair is mounted any more, the host uses its
own real sealing key.

  - the manager is a model-access holder deployed first, so its bound facts (carrying the node
    public key) are delivered; the consumer is added only once an access token exists to seal.
  - adopt reads the node public key from the bound facts; the test asserts the host mounts the
    cleartext refresh token for the manager, and that it reaches nowhere on the consuming node.
  - the refresh_grant assertion reads { sealed, manager_key }.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-07 01:55:28 +02:00

48 lines
2.3 KiB
YAML

# One machine that becomes a mesh, then plays out the whole model-access refreshable-grant flow for
# Anthropic (novox/hq ADR 0050) with the vendor's OAuth endpoint STUBBED — no real Anthropic is
# reached. The bed proves the one property the carve-out rests on: the refresh token is delivered ONLY
# to the manager node — as an ordinary sealed credential the HOST unseals — the control plane seals and
# delivers only the ACCESS token, and a consuming node writes an access-token-only credential and is
# never given a refresh token.
#
# The flow the test drives (OAuth stubbed, so it is the FLOW that is proven, not the vendor):
# the manager module seals the refresh token to the node's PUBLIC key -> the host unseals it and
# mounts the cleartext at the manager's bound path -> the manager calls the stub token endpoint ->
# submits back only { access token, re-sealed box } -> mesh-control seals the access token per
# consumer holder -> the consumer runtime writes ~/.claude/.credentials.json, access-token-only.
#
# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock
# Build BOTH runtime images into the local daemon first (the scenario stocks and serves them by
# digest, which is where the host pulls them from):
# scripts/build-module-runtime.sh anthropic-manager /tmp/anthropic-manager.tar
# scripts/build-module-runtime.sh anthropic-consumer /tmp/anthropic-consumer.tar
# (the tar output is incidental — the build also tags the image into the local docker daemon, which
# is what raise() stocks.) The stub OAuth endpoint is a tiny node server the test runs from the
# manager runtime image itself, so no extra image is needed.
scenario: anthropic-bed
segments:
hosting:
kind: public
cidr: [192.0.2.0/24]
machines:
anchor:
at: { segment: hosting, address: [192.0.2.10] }
inbound: allow
memory: 3GiB
cpus: 2
images:
# The first-node substrate: store, broker, control.
- postgres:17-alpine
- cloudamqp/lavinmq:latest
- mesh-control:development
# The two model-access runtimes, built by scripts/build-module-runtime.sh into the local daemon and
# stocked into the scenario's own registry, which is where the host pulls them from.
- mesh-runtime-anthropic-manager:development
- mesh-runtime-anthropic-consumer:development
place:
all: [host, runtime]