C-1/C-2 introduced `GraphRouting` and pointed the middleware at it.
This commit removes the legacy shape that's now dead:
* `ServerMode` enum — deleted. Single mode's `uri` lives on
`handle.uri`; multi mode's `config_path` lives on the
`GraphRouting::Multi` arm.
* `AppState.mode: ServerMode` field — deleted.
* `AppState.registry: Arc<GraphRegistry>` field — deleted. Multi
mode's registry is on `GraphRouting::Multi { registry, .. }`;
single mode has no registry at all.
* `AppState::mode()`, `AppState::uri()`, `AppState::registry()`
accessors — deleted. New `AppState::routing() -> &GraphRouting`
is the single public entry point.
* `SINGLE_GRAPH_KEY_ID` constant — deleted. `GraphHandle.key` is
still required by the struct, but in single mode the key is now
only a tracing label (`"default"`, inlined with a comment naming
its sole remaining purpose). Single-mode flat routes never carry
a `{graph_id}` parameter, so the key is never compared against
user input, and there is no registry where it could be a map
key. C-1/C-2 already removed the registry walk that the sentinel
was named for.
Callers migrated:
* `build_app` (lib.rs:944) — matches on `state.routing()` instead
of `state.mode()`.
* `server_graphs_list` (lib.rs:1162) — destructures the Multi arm
to get the registry; Single arm short-circuits to 405.
* `server_openapi` (lib.rs:1217) — matches the Multi arm for the
cluster-prefix rewrite.
* `tests/server.rs:3735` — the B2 footgun regression test now
matches on `state.routing()` to extract the single-mode handle
(the test's earlier `state.registry().list().next()` shape was
the closest pre-fix analog to "embedded consumer reaches the
engine"; the new shape is more direct).
Closes the entire "single mode forced through a multi-mode
abstraction" class. After this commit:
* No magic sentinel as a routing key.
* No `single_mode_handle` walk-and-assert helper.
* No 500-class "programmer error" branch in the middleware.
* No two-field discriminant on `AppState` where one would do.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7 KiB
Omnigraph v0.7.0
Multi-graph server mode (MR-668). One omnigraph-server process can now serve 1–10 graphs concurrently behind cluster routes (/graphs/{graph_id}/...), with per-graph and server-level Cedar policy, read-only GET /graphs enumeration, and CLI parity (omnigraph graphs list).
Runtime add/remove (POST /graphs, DELETE /graphs/{id}, omnigraph graphs create) is not in v0.7.0. Operators add or remove graphs by editing omnigraph.yaml and restarting. The first cut of POST /graphs shipped behind an atomic-YAML-rewrite design that we pulled before release once its concurrency guarantees were challenged (flock-on-renamed-inode race, duplicate-check outside the critical section, and an init-cleanup path that could destroy an existing graph's schema on re-init). The correct fix is a Lance-style cluster catalog (reserve → init → publish with recovery sidecars); that work is deferred.
Breaking Changes
- Multi-graph deployments lose flat routes. Single-graph invocation (
omnigraph-server <URI>) is unchanged — same flat/snapshot,/read,/branches, etc. Multi-graph deployments serve those routes under/graphs/{graph_id}/...; bare flat paths return 404 in multi mode. ServerConfigshape change (programmatic embedders only):ServerConfig { uri, policy_file }is replaced byServerConfig { mode: ServerConfigMode }, whereServerConfigMode = Single { uri, policy_file } | Multi { graphs, config_path, server_policy_file }. Callers that useload_server_settingsare unaffected; callers that constructServerConfigdirectly need to wrap their fields inServerConfigMode::Single.AppState's routing surface isAppState::routing() -> &GraphRouting, whereGraphRouting = Single { handle } | Multi { registry, config_path }. The previousAppState::uri(),AppState::mode(),AppState::registry()accessors and theServerModeenum are gone — embedders readstate.routing()and match on the arm they need. Per-graph URIs live onhandle.uri.AppState::new_multiis the new multi-graph constructor. Single-modenew_*/open_*constructors are unchanged.AuthenticatedActor(Arc<str>)→ResolvedActor { actor_id, tenant_id, scopes, source }(programmatic embedders only). The struct shape changes, but the HTTP contract — bearer auth, MR-731 spoof defense — is unchanged. Cluster-mode call sites construct withtenant_id: None,scopes: vec![Scope::Full],source: AuthSource::Static. Forward-compat for Cloud mode (RFC 0003) and OAuth provider (RFC 0004).
New
- Multi-graph mode. Invoke with
omnigraph-server --config omnigraph.yamlwhere the YAML has a non-emptygraphs:map and no single-mode selector (noserver.graph, no CLI<URI>or--target). At startup the server opens every configured graph in parallel (bounded concurrency, fail-fast). GET /graphs. Lists every registered graph, sorted alphabetically bygraph_id. Auth-required when bearer tokens are configured; Cedar-gated byPolicyAction::GraphListagainstOmnigraph::Server::"root". Returns 405 in single mode.- CLI
omnigraph graphs list. Mirrors the HTTP surface. Rejects local URI targets with a clear message — for remote multi-graph servers only. - Per-graph Cedar policy. Each entry in the
graphs:map can carry apolicy.filepath, loaded at startup. Cedar'sOmnigraph::Graph::"<graph_id>"resource is per-graph; the newOmnigraph::Server::"root"resource governs server-level actions. - Server-level Cedar policy.
server.policy.filein the config governs thegraph_listaction onOmnigraph::Server::"root". Required to exposeGET /graphsonce bearer tokens are configured (MR-723 default-deny otherwise rejectsgraph_listas non-read). - Cedar action vocabulary:
graph_list(server-scoped). Runtimegraph_create/graph_deleteare reserved but not shipped — see "Deferred."
Configuration
omnigraph.yaml schema additions (all optional, single-mode unaffected):
server:
bind: 0.0.0.0:8080
policy:
file: ./server-policy.yaml # server-level Cedar (graph_list)
graphs:
alpha:
uri: s3://tenant-bucket/alpha
policy:
file: ./policies/alpha.yaml # per-graph Cedar
beta:
uri: s3://tenant-bucket/beta
# no per-graph policy → engine-layer enforcement is a no-op
Deferred
POST /graphsruntime graph creation and CLIomnigraph graphs create. Pulled before release after the YAML-rewrite design's correctness story didn't survive review. A future release will add a managed cluster catalog (Lance-backed reserve → init → publish with recovery sidecars) and re-expose runtime creation on top of it. Until then, operators add graphs by editingomnigraph.yamland restarting.DELETE /graphs/{id}. Never shipped in v0.7.0; deferred with the same cluster-catalog work.StorageAdapter::delete_prefix. The substrate primitive a managed catalog would need. Will land alongside runtime mutation.X-Actor-Idservice delegation forwarding. Needs durable both-actor audit on_graph_commits.lance— out of scope.- Hot policy reload. Restart is cheap at N≤10 graphs.
User Impact
- Existing single-graph deployments upgrade with zero changes.
omnigraph-server <URI>with v0.6.0 config keeps working identically. - Multi-graph adoption is opt-in. Add a
graphs:map toomnigraph.yaml(and removeserver.graph) to switch a deployment to multi mode. - Cluster routes are breaking for client SDKs targeting multi mode. Generated clients from previous v0.6.0 OpenAPI specs will hit 404 on flat paths against a multi-mode server. Regenerate against the v0.7.0
openapi.json. - Operator-supplied policy.yaml files don't change. The Cedar
Omnigraph::GraphandOmnigraph::Serverentities are internally generated bycompile_policy_source— operator YAML only references actions and groups.
Migration: single → multi
# Before (v0.6.0 single-mode invocation)
server:
graph: my-graph
graphs:
my-graph:
uri: /var/lib/omnigraph/my-graph
policy:
file: ./policy.yaml
# After (v0.7.0 multi-mode — drop `server.graph` and the top-level `policy`)
server:
policy:
file: ./server-policy.yaml # NEW: governs GET /graphs
graphs:
my-graph:
uri: /var/lib/omnigraph/my-graph
policy:
file: ./policy.yaml # MOVED: was top-level
Same omnigraph.yaml file; restart the server. Clients targeting the old flat routes (/snapshot, /read, …) must update to /graphs/my-graph/snapshot, etc.
To add a new graph after rollout: stop the server, append a new graphs.<id> entry, restart.
Test coverage
GraphIdnewtype validation, registry race tests (PR 3), init failpoints (PR 2a — still reachable fromomnigraph initCLI).- Mode-inference four-rule matrix (PR 5), parallel multi-graph startup, cluster routing.
- Cedar
Serverresource refactor, backwards-compat for graph-only policies. GET /graphsenumeration, 405-in-single-mode.- MR-731 spoof regression test stays green across the entire refactor.