12 KiB
Omnigraph v0.6.0
Two pieces of work land in this release:
- The graph terminology rename (renamed
Repo→Graphacross the Cedar resource model, policy API, and query-lint schema source). - Multi-graph server mode — one
omnigraph-serverprocess can now serve 1–10 graphs concurrently behind cluster routes (/graphs/{graph_id}/...), with per-graph and server-level Cedar policy, read-onlyGET /graphsenumeration, and CLI parity (omnigraph graphs list).
Runtime add/remove (POST /graphs, DELETE /graphs/{id}, omnigraph graphs create) is not in v0.6.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
Graph terminology rename
- Renamed the Cedar resource entity from
Omnigraph::RepotoOmnigraph::Graph. - Renamed policy API terminology from
repo_idtograph_idonPolicyCompiler::compile(and on the newPolicyEngine::load_graph/PolicyEngine::load_serverloaders described below). - Renamed query-lint schema source JSON from
"repo"to"graph"forschema_source.kind.
Multi-graph server mode
- 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 and the bearer-derived-actor-identity guarantee — is unchanged. Cluster-mode call sites construct withtenant_id: None,scopes: vec![Scope::Full],source: AuthSource::Static. The new fields are forward-compat seams for future multi-tenant and OAuth deployments; they're inert in this release.PolicyEngine::load(path, graph_id)removed in favor of two kind-typed loaders:PolicyEngine::load_graph(path, graph_id)for per-graph policies andPolicyEngine::load_server(path)for server-level policies. Each loader rejects rules whose actionresource_kind()doesn't match the engine kind — operators who put agraph_listrule in a per-graph file (or areadrule in a server file) now get a load-time error instead of a silently-never-matching rule.PolicyRequest::actor_idfield removed. Actor identity is now a separate parameter onPolicyEngine::authorize(actor_id, &request). The type system enforces the server-authoritative-actor invariant: actor identity is always sourced from the bearer-token match resolved at the auth boundary; handlers cannot smuggle identity through the request body.Omnigraph::initis strict by default. Initialization at a URI that already holds schema files now errors withOmniError::AlreadyInitializedinstead of silently overwriting. Operators who actually want to overwrite useInitOptions { force: true }(CLI:omnigraph init --force). Closes the destructive-cleanup footgun where a failed re-init would delete an existing graph's schema files.- Top-level
policy.fileis rejected in multi-graph server mode. It remains valid for single-graph / CLI-local policy. Multi-graph deployments must move graph rules tographs.<graph_id>.policy.fileand server-scopedgraph_listrules toserver.policy.file. - Open server startup requires explicit opt-in. A server with no bearer tokens and no policy now refuses to start unless passed
--unauthenticatedorOMNIGRAPH_UNAUTHENTICATED=1. - Policy requires bearer tokens. Configuring any policy file without bearer tokens now refuses startup; otherwise every protected request would 401 before Cedar could evaluate it.
- Tokens without policy default-deny non-read actions. Existing authenticated deployments that relied on writes or admin routes without Cedar policy must add policy rules for those actions.
GET /graphsrequiresserver.policy.filein every runtime state. Even--unauthenticatedmode keeps server topology closed until the operator explicitly authorizesgraph_list.
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. Server-scoped actions require an explicitserver.policy.filein every runtime state — the management surface is closed by default even in--unauthenticatedmode so that server topology is never exposed without operator opt-in.- CLI
omnigraph graphs list. Mirrors the HTTP surface. Rejects local URI targets with a clear message — for remote multi-graph servers only. - CLI
omnigraph init --force. Bypasses the strict-init preflight when an operator deliberately wants to recover from orphan schema files. Does NOT purge existing Lance datasets; recursive deletion needsStorageAdapter::delete_prefix(deferred — see below). - Per-graph Cedar policy. Each entry in the
graphs:map can carry apolicy.filepath, loaded at startup viaPolicyEngine::load_graph. 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 /graphsin every runtime state — without a server policy the default-deny posture rejectsgraph_list, including in--unauthenticatedmode. - Cedar action vocabulary:
graph_list(server-scoped). Runtimegraph_create/graph_deleteare reserved but not shipped — see "Deferred." - Canonical graph URI identity. Server startup normalizes graph root URIs before registry insertion and response output, so aliases such as
/tmp/g,/tmp/g/, andfile:///tmp/gcannot register as distinct graphs that actually share one Lance root.
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.6.0; deferred with the same cluster-catalog work.StorageAdapter::delete_prefix. The substrate primitive a managed catalog would need. Will land alongside runtime mutation.omnigraph init --forcepurging Lance state. Today--forceonly bypasses the schema-file preflight; recursive deletion of existing Lance datasets needsdelete_prefix.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
- No on-disk migration is required. Existing
.omnigraphs from v0.5.0 (and earlier) open cleanly under v0.6.0 — Lance datasets,__manifest,_schema.pg,_schema.ir.json,__schema_state.json,_graph_commits.lance,_graph_commit_recoveries.lanceall use unchanged formats. No conversion step. - Existing single-graph storage upgrades without migration. Server deployments may need auth/policy config changes: explicitly pass
--unauthenticatedfor local open mode, configure tokens when using policy, and add Cedar policy for non-read authenticated actions. - 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.5.0 OpenAPI specs will hit 404 on flat paths against a multi-mode server. Regenerate against the v0.6.0
openapi.json. - Supported YAML policy authoring is unchanged. The Cedar
Omnigraph::GraphandOmnigraph::Serverentities are internally generated bycompile_policy_source— operator YAML only references actions and groups. - Operators with unsupported raw Cedar policy files should update
Omnigraph::Reporesource references toOmnigraph::Graph.
Migration: single → multi
# Before (v0.5.0 single-mode invocation)
server:
graph: my-graph
graphs:
my-graph:
uri: /var/lib/omnigraph/my-graph
policy:
file: ./policy.yaml
# After (v0.6.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.
Documentation
- Public docs, CLI help, examples, server docs, and test helpers now consistently use "graph" for the OmniGraph data artifact.
- GitHub/source repository terminology remains spelled out as "repository" where needed.
- New:
docs/user/cli.mddocumentsomnigraph graphs list;docs/user/server.mddocuments the multi-graph mode and the cluster route convention;docs/user/policy.mddocuments the per-graph vs server-scoped action distinction.
Test coverage
GraphIdnewtype validation, registry race tests, init failpoints (still reachable fromomnigraph initCLI).- Mode-inference four-rule matrix, parallel multi-graph startup, cluster routing.
- Cedar
Serverresource refactor, backwards-compat for graph-only policies, kind-alignment rejection (server actions in graph files / vice versa). GET /graphsenumeration, 405-in-single-mode, 403-in-Open-mode-without-server-policy, Cedar admin/viewer authorization.- Cluster routes with inner path params (
/branches/{branch},/commits/{commit_id}) deserialize correctly under axum 0.8 nested routing. - Policy-requires-tokens startup invariant enforced uniformly across single and multi mode.
- The bearer-auth-derived-actor-identity regression test (client-supplied identity headers are ignored; the server-resolved actor is the only identity Cedar sees) stays green across the entire refactor.