Architecture v0.4.0 — console polish, docs, and proof
Scope from contract §12 (binding) and decisions D21–D23. Everything from v0.3.1 plus the changes below. The transport is unchanged: JSON-RPC 2.0 over gRPC, exactly as §11 fixed it in 0.3.1.
0.4.0 is in progress
This page states the scope the contract fixes, not results. Where a claim needs evidence — multiple workers
across multiple zones, autoscaling and rebalance under load, per-zone tool isolation, independent MCP clients,
the role and group security matrix — the evidence belongs in the release's test-status report
(reports/test-status-v0.4.0.md), which is still being filled in — the GKE run is pending. Nothing on this
page should be read as a verified result ahead of that report.
Console
Nothing in the data path changed; this is the management surface.
- Per-zone packages (§12.1). Tools, resources and prompts are listed per zone, each with an enable/disable
toggle for that zone.
environments[].blockedstays the environment-wide list from §9; a new per-zone mapenvironments[].blocked_zones = {zone: [names]}adds to it, and a deploy writes the union of both into that zone'sRAMEN_BLOCKED. Route:PUT /api/v1/groups/{group}/environments/{env}/zones/{zone}/blocked. The enforcement point does not move — the node still filters*/listand answers-32601— so a model connected to one zone sees only what that zone has switched on. - Key client types (D21). Every key now carries a
client_typethat is enforced on both sides.devopskeys (rmn_) are accepted by/api/v1/*and rejected by workers;agentkeys (rmk_) are accepted by a worker'sramen.v1.Mcpfor the groups and zones they name and rejected by the console API with 403. Keys issued before 0.4.0 are read asdevopswhen they startrmn_andagentotherwise. The two key kinds were always separate in practice; 0.4.0 makes the separation a checked property rather than a convention. - Session epoch (folded in from the 0.3.0 audit backlog). Each user document carries
session_epoch, sessions embed it, and a session whose epoch differs is rejected. It is bumped on password change, role change, group change, delete, and on any change toconfig/auth— so disabling password login, changing someone's role or deleting a user takes effect on their live sessions instead of at the next expiry. - Passwords and generated keys are at least 12 characters with upper, lower, digit and special characters,
enforced both when they are generated and when a password is set or changed (
RAMEN_MIN_PASSWORD_LENmay raise the floor, not lower it). - Naming and presentation. The product name appears once, in the logo. Every button, label, heading and
message is sentence case, with no abbreviations in user-visible text ("service account", never "SA") and no
colour names in health wording — the colour stays the signal and each cell carries a text label, so the grid
reads without colour. The group page collects every per-zone action into one
Actionssection, the logs page becomes two panes (entry list with the newest selected, body beside it), and the sidebar shows the signed-in email and role together under the logo.
Node
The transport is unchanged, but two things about how the node reads its environment are not.
RAMEN_TRUST_PROXY_HOPSreplaces the booleanRAMEN_TRUST_PROXY. The client address is the n-thx-forwarded-forentry counted from the right — the end proxies append to — so entries a caller supplies cannot become the address the allowlist checks. Deployed values are2on GCP and1on AWS, set by the worker chart and both console renderers; the default is0, meaning the peer address, which is right for local and compose.RAMEN_TRUST_PROXY=1still means one hop, and an explicit hop count overrides it in either direction. Every failure mode — trust off, no header, too few entries, an unparseable entry — falls back to the peer address, so a wrong count denies rather than admits.RAMEN_REFLECTIONgates gRPC server reflection. It defaults to on and is set to0on deployed workers by the chart and both renderers, because reflection runs ahead of every guard and would let an anonymous caller at the edge enumerate the services,ramen.v1.Adminincluded. Local and compose workers keep it, sogrpcurlstill works there without a copy of the proto.
Both came out of the independent review of the 0.4.0 transport claims, and both postdate the 0.3.2 cloud run, so neither has been exercised against a real load balancer yet.
Docs site
- Dark only. One palette, no light/dark toggle (
scheme: slate, custom primary and accent), with the tokens indocs/stylesheets/ramen.css. Contrast was measured rather than assumed: coral#F26B3Aon the ink background#1F1B18is 5.65:1 and passes WCAG AA, so the brand token was kept; the header bar's old white text on coral was 3.03:1 and failed, so the header and tabs became an ink surface with a coral rule and a coral active-tab colour. - A real diagram.
docs/img/architecture.svgis hand-written inline SVG — no mermaid, no external renderer — and replaces the ASCII shape on How it works. It draws the path that actually runs: stdio client →ramen-mcp-bridge→ gRPC through the load balancer onramen-group/ramen-zonemetadata → Rust node → Python runtime → group bucket, with the console alongside. It names the hops that are plaintext HTTP/2 unless TLS is configured, because a diagram that implies end-to-end encryption would be wrong. - Transport written down. Transport and what secures each hop states, per hop, what protects it and what does not, and separates what was verified on a live cluster from what has only unit and conformance tests — and from the AWS path, which had still never been applied to a real account (no longer true from 0.5.6).
- Smaller things. The edit-this-page action and
edit_uriare gone;llms.txtis still served for agents but has no navigation entry; heading permalinks are off, so headings no longer render a leading§; the badge row is one row at one height from one source.
Repository metadata
The GitHub repository carries a description, the documentation site as its homepage, and the twenty topics drafted
for the launch (mcp, model-context-protocol, mcp-server, ai-agents, llm-tools, kubernetes, gke,
eks, gcp, aws, rust, python, fastapi, self-hosted, high-availability, canary-deployment,
terraform, helm, grpc, json-rpc), so that people and language models can find it.
Verification planned for this release
Listed here so the gap is visible, not to claim it is closed. Per §12.3 and D22/D23:
| What | How it is meant to be shown |
|---|---|
| Multiple workers serving across multiple zones | a local kind cluster with two worker namespaces, then once on a throwaway GKE project |
| Autoscaling under load and rebalance moving traffic | generated load against a zone, an HPA scaling it up and back down, rebalance changing the split |
| Per-zone tool isolation | the package lists two zones return differing after a per-zone toggle |
| Independent MCP clients | a client driven against the bridge and screenshotted, plus a published editor config |
| Roles and groups | a matrix over roles × groups × both key types, asserting every cross-group and cross-role action is refused and that revocation is immediate |
| The transport claims above | an independent review of the claims against the code, filed separately |
Results, when they exist, go in reports/test-status-v0.4.0.md and the cloud run in reports/cloud-v0.4.0.md.
Decisions added in this release
| ID | Decision |
|---|---|
| D21 | API keys carry a client type that is enforced: an agent key is accepted only by workers over gRPC, a devops key only by the console API; each is rejected by the other side. |
| D22 | The autoscale and rebalance stress test runs on a local kind cluster first, then once on a throwaway GKE project. |
| D23 | Independent-client evidence is a real MCP client driven against the bridge, plus an editor configuration the user captures. |