Skip to content

feat(server): serve a cluster to the same account on loopback - #1077

Closed
ragnorc wants to merge 16 commits into
ModernRelay:mainfrom
ragnorc:feat/local-cluster-server
Closed

ragnorc wants to merge 16 commits into
ModernRelay:mainfrom
ragnorc:feat/local-cluster-server

Conversation

@ragnorc

@ragnorc ragnorc commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Disposition — 2026-10-20

The second serving model is discontinued. OmniGraph retains one cluster-backed
server model, including on localhost: the ordinary applied state, authentication,
policies, admission, routes and deployment lifecycle.

This PR is closed without merging. The standalone/local serving APIs, generated
local policies and credentials, and local-server discovery/election/reuse
registry are not proceeding. This decision supersedes the implementation and
sequencing proposed below; draft #1017 is being revised accordingly. No part of
this serving stack has shipped. General improvements to the existing server
may be proposed independently without introducing another serving mode.

Historical proposal

Stacked on #1075 (standalone local servers); review only the last commit.

What & why

LocalScope::Cluster serves a cluster's storage root from its applied state for one actor, with the cluster's own policies, stored queries, embedding-provider bindings and external-Blob rules:

  • Read-only captures one ledger revision without admission, opens every graph read-only and validates it against that revision through GraphReadAuthority (a changed revision or an outstanding deployment refuses), and adds a Cedar forbid for every write action to each policy it loads, so policy answers report the refusal (rule local-read-only).
  • Read-write takes the cluster's serving admission and refuses while another owner holds it (state_lock_held). serve_local releases it after a clean drain, through the release the deployment runs (now one shared function), and keeps it when a completion is uncertain.

PolicyEngine::forbidding adds the forbid; a prepared graph open now names its access (writable within a storage scope, or read-only against a captured revision).

Backing issue / RFC

Checklist

  • Change is focused (one logical change)
  • Tests: policy a_forbidden_action_is_denied_whatever_permits_it; in-source read-only/read-write class consistency; auth_policy read-only (no lock, forbid decided, writes refused) and read-write (lock held, second owner refused, reader beside it, writes as its actor); boot_settings::local_serving cluster mode (lock while serving, released on a clean shutdown)
  • Public docs: control-plane public embedding APIs; changelog.d/local-cluster-server.added.md
  • Reviewed against docs/dev/invariants.md: the read-only consumer takes no admission and writes nothing; admission semantics are the deployment's

Local verification

  • RUST_MIN_STACK=16777216 cargo test -p omnigraph-server -p omnigraph-policy — all pass; after the last change (deriving the action list from the enum) the local lib and integration tests were re-run
  • cargo clippy -p omnigraph-server -p omnigraph-policy --all-targets --locked -- -D warnings -W clippy::dbg_macro, cargo fmt --all --check, python3 scripts/check-docs.py, typos — clean

Notes for reviewers

A read-write local server of a cluster whose graphs all fail to open keeps the admission it took, as a deployment does at startup.

AppState::local opens a standalone graph read-only or read-write behind a
generated bearer token and a Cedar policy generated for one actor, which
it installs on the engine as well as at the HTTP layer. Every policy
action has a local-server class, so an action added later is never
granted until it is given one. A read-only local server refuses every
write-class route in ingress before reading its body. No local server
mounts the cluster deployment routes; each enables MCP ad-hoc reads and
lists write tools only when it writes. A root inside a cluster is
refused.

serve_local runs the deployment's serving lifecycle, now one private
type both entry points share, on a loopback listener the caller bound.
It writes nothing to stdout, disarms the shutdown watchdog when it
returns, and reports an uncertain outcome instead of exiting.
install_log_subscriber puts the server's restricted log filter on
another writer.
LocalScope::Cluster serves a cluster's storage root from its applied
state for one actor, with the cluster's own policies, stored queries,
embedding-provider bindings and external-Blob rules. A read-only one
captures one ledger revision without admission, opens every graph
read-only and validates it against that revision, and forbids every
write action in each policy it loads, so policy answers report the
refusal. A read-write one takes the cluster's serving admission and
refuses while another owner holds it; serve_local releases it after a
clean drain through the release the deployment runs, and keeps it when
a completion is uncertain.

PolicyEngine::forbidding adds a Cedar forbid for a set of actions, and a
prepared graph open now names its access: writable within a storage
scope, or read-only against a captured ledger revision.
@ragnorc
ragnorc marked this pull request as ready for review October 18, 2026 20:30
@ragnorc
ragnorc requested a review from aaltshuler as a code owner October 18, 2026 20:30
The shutdown listener registered its SIGTERM and SIGINT handlers only
when its spawned task first ran, and `serve_local` installed the
lifecycle only after the caller had announced readiness. A signal in
either window met the default disposition and ended the process without
the drain.

The lifecycle now registers both handlers before it returns, and
`start_local` installs it so a caller announces between `start_local`
and `LocalServing::serve`. `serve_local` remains the two calls in one.
…server

# Conflicts:
#	crates/omnigraph-server/src/lib.rs
#	crates/omnigraph-server/tests/boot_settings.rs
#	docs/dev/control-plane.md

ragnorc commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

This commit adds LocalScope::Cluster on the standalone local server from #1075. One process can serve a cluster's applied state on loopback to the same OS account.

Read-only capture takes no cluster lock. It opens each graph with GraphReadAuthority and adds a Cedar forbid for write actions. Read-write takes serving admission, the same door a deployment uses. serve_local releases that admission after a clean drain and keeps it when the outcome is uncertain.

Request changes. Both accesses drop the boot witness. A read-only open returns success when every graph fails. A cluster with no policy bundle leaves the engine open to writes.

Blocking

1. /readyz reports ledger revision 0

settings_from_snapshot builds a BootWitness from the applied snapshot. serve_config installs it with with_boot_witness. Both arms of local_cluster keep only config.mode. new_multi_entries then stores BootWitness::default().

I opened a converged cluster whose __cluster/state.json has state_revision 5. GET /readyz returned 200 for read-only and for read-write:

{"ready":true,"status":"serving","state_revision":0,"served_graph_count":1,"ready_graph_count":1,"loading_graph_count":0,"blocked_graph_count":0,"shutdown_grace_seconds":25}

The body has no state_cas and no booted_serving_digest. Those fields match the default witness. A client that trusts readiness cannot tell which revision this process booted.

Call with_boot_witness with config.witness before into_local, on both accesses. Assert /readyz in the existing auth_policy cluster tests. The revision, the CAS, and the serving digest must match the snapshot.

Inline, crates/omnigraph-server/src/local.rs:441: both arms drop config.witness. serve_config installs that witness before it listens. This tail calls into_local on a state whose witness is still the default.

2. A read-only server returns Ok when every graph fails to open

finish_graph_startup returns an error when every configured graph is blocked. The read-write arm calls it. The read-only arm records each open failure and each validate_opened failure as a blocked entry and returns Ok(state).

I deleted graphs/knowledge.omni on a converged one-graph cluster. AppState::local with ReadOnly returned Ok. A deployment serve returns an error and does not listen. A caller that treats Ok as ready will publish a server whose /readyz status is blocked.

Use the same all-failed check. One blocked graph beside a ready graph can still return Ok. Zero ready graphs must be LocalOpenError.

GraphReadAuthority::capture already refuses the whole server on a changed revision. A validate_opened failure only blocks that graph. Say that in docs/dev/control-plane.md. The current sentence says the authority refuses a changed revision, and the server still starts when only the later check fails.

Inline, crates/omnigraph-server/src/local.rs:529.

3. A cluster with no policy bundle leaves the engine open

forbid_writes runs only when a bundle loads. prepare_single_graph does the same for each graph. Omnigraph::enforce returns success when policy is None. OpenMode::ReadOnly skips open-time writes and does not fence later mutations.

I opened a cluster with an empty policy section. local() returned Ok. The knowledge handle had policy.is_none() == true. POST /graphs/knowledge/mutate returned 403 from ingress, with this local server is read-only. The HTTP door holds. The engine door does not. An embedder that calls the handle can change the graph.

The draft RFC says a local state is never in open or default-deny mode, and that the engine refuses every write.

Fix this in the policy layer. When the server scope or a graph has no bundle, install a policy that permits the read actions a deployment allows with no bundle, and forbids every write action. Then enforce denies writes. An empty forbid-only policy also denies reads. Do not install that.

Inline, crates/omnigraph-server/src/local.rs:509.

Optional

  1. Errors after admit_serving_snapshot, and before a clean serve_local, drop ClusterAdmission. Drop does not unlock. release_settings_refusal releases when settings fail, because no graph open has started. prepare_multi_graph_state can still fail while it holds the admission in a local variable (policy compile, a bad graph id, validate_serving) and drop it. set_catalogue_policy and finish_graph_startup fail after the admission sits on the state. The error is LocalOpenError::Serving(String) and carries no lock id. The next local() then fails with state_lock_held until force-unlock. Release on failures that happen before any graph open, as the settings path does. When a failed open keeps the lock, put the lock id in the error. Keeping the lock after every graph fails matches the admission contract. Name the lock for the embedder. Inline, crates/omnigraph-server/src/local.rs:423.

  2. finish_graph_startup is called with require_all_graphs: false. settings_from_snapshot sets config.require_all_graphs from OMNIGRAPH_REQUIRE_ALL_GRAPHS, because this call passes deployment = false. The flag can refuse the settings projection and still allow a later partial open.

  3. The catalogue policy does not pass through forbid_writes. Catalogue routes today are query and schema reads. A later write route would inherit a policy that still permits writes.

Where the code and the RFC differ

RFC head 7c72c7e7ed2b42fb0e5b3f5140d8235444ddcf33 (PR #1017, still open).

Sibling pull requests

The review target is commit cc68d0c3 only. The parent is #1075 at 16de8b9d.

This commit composes with that parent. into_local, the loopback guard, ingress write refusal, and MCP allow_writes follow LocalAccess. I did not re-review the standalone policy generator.

Merge after #1075. The combined diff touches crates/omnigraph-server/src/lib.rs and tests/integration/auth_policy.rs. #1073, #1074, #1076, and #1078 also edit those files. #1076 and #1078 also edit ingress.rs. This commit does not.

#1080 and #1081 are CLI targets and access / whoami. #1059 is --as. #1012 is the branch listing. Draft #675 (RFC 0059) is still a draft. This library takes a root, not a named target. Findings for those belong on those pull requests.

Tradeoff and liability

One serving projection and one release function is the right shape. A second cluster server would add a second contract to keep true.

AppState::local for a cluster is a new public door. Read-write holds a lock that Drop does not release. An embedder that drops the state, or that hits an error after admission, leaves __cluster/lock.json in place. A deployment process exits. This caller stays alive. That is new operational liability.

A read-only server takes no admission, so cluster apply can run beside it. Policies, stored queries, embedding bindings, and Blob rules stay at the captured revision. Graph handles still follow the branch head. Report the booted revision on /readyz so an operator can see which ledger those policies came from.

The witness and the all-failed check already exist on the deployment path. Use them here. A separate startup type for local servers would fork the contract again on the next scope.

The forbid-on-loaded-policy design is right when a bundle exists. The tests prove that case. A cluster with no bundle is a supported deployment mode, and this path skips it.

Validation

Head cc68d0c32472e42c536604d98484644894814cc1. Parent 16de8b9ddf5ac79f7f8114ffc48701388ca46383. Base 0a62bb7b2aa5b99b012f413807c216277f7f9596.

Commands from an isolated checkout at that head, with CARGO_TARGET_DIR=/workspace/target, RUST_MIN_STACK=16777216, and --locked:

  • cargo test -p omnigraph-policy --lib a_forbidden_action_is_denied_whatever_permits_it — 1 passed, exit 0
  • cargo test -p omnigraph-server --lib local:: — 3 passed, exit 0
  • cargo test -p omnigraph-server --test integration local_cluster — 2 passed, exit 0
  • cargo test -p omnigraph-server --test boot_settings local_serving:: — 4 passed, 1 ignored (instrument: helper), exit 0

CI on this head (run 39160414172 and the GQT and DST runs) is green, including Test Workspace, clippy, rustfmt, GQT, and DST. Devin Review skipped a full review (trial expired).

A temporary probe, temp_probe_local_cluster_contract, ran in that checkout and was removed. It was not pushed.

  • Ledger state_revision was 5. Read-only and read-write /readyz were both 200 with state_revision 0 and no CAS or digest.
  • A policy-less cluster had policy.is_none() == true. Mutate returned HTTP 403 with the read-only message.
  • After deleting graphs/knowledge.omni, read-only local() returned Ok.

This pull request is a feature. GQT cannot express admission, process lifecycle, or /readyz. I am not asking for a .gqt case. Add the three assertions above to the auth_policy owner.

Limitations

I did not rerun the full workspace suite locally. CI did, on this head. I did not run this path against S3 or Azure. I did not re-read Lance storage pages. This commit does not change Lance behavior. I checked open_read_only, enforce, and ClusterAdmission drop in OmniGraph source. The uncertain-completion test in boot_settings uses a standalone graph, so I did not execute an uncertain cluster completion. The release branch is the same match a deployment uses.

…ne's policy

- A relative or URL-special local root now names its cluster.
  `LocalBinding::from_root` makes a local root absolute, and the cluster
  storage URI is built with `Url::from_file_path`, so `./cluster` no longer
  becomes `file://./cluster`, which the cluster store refused in both modes.
- A ledger revision that moves between a read-only open's capture and its
  validation refuses the whole server. `GraphOpenFailure` carries the
  cluster's refusal from `validate_opened`, and the read-only open turns it
  into `LocalOpenError::Cluster`. A graph whose schema drifted from its
  achieved contract is still blocked alone, as normal serving blocks it.
- A user rule can share an id with the generated forbid. Cedar policy ids
  are namespaced `permit:` and `forbid:`, so the two can no longer collide
  and fail `PolicySet::add`. A matched rule still reports the bare rule id.
- `LocalServing::release` stops a local server that never served. It closes
  request admission, drains the logical owners, releases a read-write
  cluster's serving admission as `serve` does after a clean drain, and
  disarms the watchdog. Before this, a caller that failed after
  `start_local` could only drop it, which keeps the admission lock.
- `/readyz` and `/graphs` report the ledger revision served. Both access
  modes install the boot witness from the applied snapshot instead of the
  default revision 0.
- A local cluster server refuses when no graph opens, as a deployment's
  startup does. Read-only now applies the same check as `finish_graph_startup`,
  and both modes honour `OMNIGRAPH_REQUIRE_ALL_GRAPHS` through the config's
  `require_all_graphs`, not a hard-coded `false`.
- A graph the cluster binds no policy to gets `read` alone for the actor,
  which is what a deployment's routes grant without a bundle. The grant is
  installed on the engine too, so an embedder holding the handle can no
  longer write through an engine with no policy.
- Read-write refusals before any graph opens release the admission, as a
  refused settings preflight does. The catalogue policy now loads before
  graph preparation. A failed startup after a graph open keeps the admission
  and names its lock in `LocalOpenError::AdmissionRetained`.
- The read-only catalogue policy passes through the same write forbid as
  every other policy it loads.

ragnorc commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

This pull request lets one process serve a cluster's applied state on loopback to the same OS account.

Read-only capture takes no cluster lock. It opens each graph with GraphReadAuthority and adds a Cedar forbid for write actions when a policy bundle loads. Read-write takes serving admission, the same door a deployment uses. LocalServing::serve releases that admission after a clean drain and keeps it when the outcome is uncertain.

The commits after cc68d0c3 merge #1075 (c087b789, 6a4b02cf). They register shutdown signals before announce, and they split serve_local into start_local and LocalServing::serve. They do not change local.rs. The three blocking findings from the review at cc68d0c3 are still open.

Request changes.

Earlier findings

Earlier finding At c04a0c85
Blocking. /readyz reports ledger revision 0 Still open
Blocking. A read-only server returns Ok when every graph fails to open Still open
Blocking. A cluster with no policy bundle leaves the engine open to writes Still open
Optional. Errors after admission, and before a clean serve, drop the lock with no lock id Still open. The merge adds a public drop window
Optional. OMNIGRAPH_REQUIRE_ALL_GRAPHS can refuse settings and still allow a partial open Still open
Optional. The catalogue policy does not pass through forbid_writes Still open

Blocking

1. /readyz reports ledger revision 0

settings_from_snapshot builds a BootWitness from the applied snapshot. serve_config installs it with with_boot_witness. Both arms of local_cluster keep only config.mode. new_multi_entries then stores BootWitness::default().

I opened a converged cluster whose __cluster/state.json has state_revision 5. GET /readyz returned 200 for read-only and for read-write:

{"ready":true,"status":"serving","state_revision":0,"served_graph_count":1,"ready_graph_count":1,"loading_graph_count":0,"blocked_graph_count":0,"shutdown_grace_seconds":25}

The body has no state_cas and no booted_serving_digest. Those fields match the default witness. A client that trusts readiness cannot tell which revision this process booted. The user server guide says /readyz reports the applied digest and the ledger revision and CAS. This door does not.

Call with_boot_witness with config.witness before into_local, on both accesses. Pass the state's existing draining flag and shutdown grace, so the lifecycle and /readyz share one flag. Assert /readyz in the existing auth_policy cluster tests. The revision, the CAS, and the serving digest must match the snapshot.

crates/omnigraph-server/src/local.rs:409 and :465: both arms drop config.witness. crates/omnigraph-server/src/local.rs:441: into_local does not install a witness. crates/omnigraph-server/src/lib.rs:854: the constructor stores the default. crates/omnigraph-server/src/lib.rs:3205: serve_config installs the real witness before it listens.

2. A read-only server returns Ok when every graph fails to open

finish_graph_startup returns an error when every configured graph is blocked. The read-write arm calls it. The read-only arm records each open failure and each validate_opened failure as a blocked entry and returns Ok(state).

I deleted graphs/knowledge.omni on a converged one-graph cluster. AppState::local with ReadOnly returned Ok. GET /readyz was 503:

{"ready":false,"status":"blocked","state_revision":0,"served_graph_count":1,"ready_graph_count":0,"loading_graph_count":0,"blocked_graph_count":1,"shutdown_grace_seconds":25}

A deployment serve returns an error and does not listen. The user server guide says a nonempty cluster whose graphs all fail refuses startup. A caller that treats Ok as ready will publish a server whose /readyz status is blocked.

Use the same all-failed check. One blocked graph beside a ready graph can still return Ok. Zero ready graphs must be LocalOpenError.

GraphReadAuthority::capture already refuses the whole server on a changed revision. A validate_opened failure only blocks that graph. Say that in docs/dev/control-plane.md. The current sentence says the authority refuses a changed revision. The server still starts when only the later check fails, and it still starts when every open fails.

crates/omnigraph-server/src/local.rs:508. The deployment check is crates/omnigraph-server/src/lib.rs:3556.

3. A cluster with no policy bundle leaves the engine open

forbid_writes runs only when a bundle loads. prepare_single_graph does the same for each graph. Omnigraph::enforce returns success when policy is None. OpenMode::ReadOnly skips open-time writes. It does not fence later mutations.

I opened a cluster with an empty policy section. local() returned Ok. The knowledge handle had policy.is_none() == true. branch_create_as("probe-read-only", Some("act-local")) returned Ok(()). POST /graphs/knowledge/mutate returned 403 from ingress, with this local server is read-only. The HTTP door holds. The engine door does not. An embedder that calls the handle can change the graph.

The draft RFC says a local state is never in open or default-deny mode, and that the engine refuses every write.

Fix this in the policy layer. When the server scope or a graph has no bundle, install a policy that permits the read actions a deployment allows with no bundle, and forbids every write action. A deployment with tokens and no bundle permits per-graph read and denies every other action at HTTP (authorize in handlers.rs). Then enforce denies writes. An empty forbid-only policy also denies reads. Do not install that.

crates/omnigraph-server/src/local.rs:471. crates/omnigraph-server/src/lib.rs:3661. crates/omnigraph/src/db/omnigraph.rs:1070.

Optional

  1. Errors after admit_serving_snapshot, and before a clean LocalServing::serve, drop ClusterAdmission. Drop does not unlock. release_settings_refusal releases when settings fail, because no graph open has started. prepare_multi_graph_state can still fail while it holds the admission in a local variable (policy compile, a bad graph id, validate_serving) and drop it. set_catalogue_policy and finish_graph_startup fail after the admission sits on the state. A non-loopback listener fails inside start_local after local() has moved the state in, and that drop keeps the lock too. The error is LocalOpenError::Serving(String) or a loopback bail, and it carries no lock id. The next local() then fails with state_lock_held until force-unlock. Release on failures that happen before any graph open, as the settings path does. When a failed open keeps the lock, put the lock id in the error. Keeping the lock after every graph fails matches the admission contract, and the pull request already says so for read-write. Name the lock for the embedder. crates/omnigraph-server/src/local.rs:403 and :415.

  2. finish_graph_startup is called with require_all_graphs: false. settings_from_snapshot sets config.require_all_graphs from OMNIGRAPH_REQUIRE_ALL_GRAPHS, because this call passes deployment = false. The flag can refuse the settings projection and still allow a later partial open. The read-only arm never calls finish_graph_startup, so the same flag cannot turn an open failure into an error. crates/omnigraph-server/src/local.rs:395 and :428.

  3. The catalogue policy does not pass through forbid_writes. Catalogue routes today are query and schema reads. A later write route would inherit a policy that still permits writes. crates/omnigraph-server/src/local.rs:424 and :526.

  4. New, from the merge with feat(server): serve one standalone graph to the same account on loopback #1075. start_local clones cluster_admission into LocalServing. LocalServing has no release on drop. ClusterAdmission's Drop does not unlock. Dropping LocalServing before serve keeps __cluster/lock.json. The new docs say that drop stops the shutdown listener and leaves the SIGTERM and SIGINT handlers installed for the rest of the process. They do not say the cluster lock stays. An embedder that drops the value after a failed announce keeps the lock, and a later SIGTERM no longer ends the process. serve after a signal still releases on a clean drain: local_serving::serve_local_runs_the_lifecycle_and_leaves_stdout_to_its_caller_cluster passed and the lock file was gone. Document the drop. Put the lock id on LocalServing so the embedder can name it. Release from that path only when serve was never called and no write owner is active. Do not unlock from Drop after a write. crates/omnigraph-server/src/lib.rs:3006 and :3036. docs/dev/control-plane.md:568.

Where the code and the RFC differ

RFC head 7c72c7e7ed2b42fb0e5b3f5140d8235444ddcf33 (PR #1017, still open, still draft).

Sibling pull requests

The review target is commit c04a0c85. The parent is #1075 at 6a4b02cf. This head contains that parent.

This commit composes with that parent. into_local, the loopback guard, ingress write refusal, and MCP allow_writes follow LocalAccess. I did not re-review the standalone policy generator or the signal-handler registration. Finding 4 is only the cluster-admission interaction with the new drop.

Merge after #1075. The combined diff touches crates/omnigraph-server/src/lib.rs, tests/integration/auth_policy.rs, tests/boot_settings.rs, and docs/dev/control-plane.md. #1073, #1074, #1076, #1078, #1080, and #1081 also edit lib.rs or auth_policy.rs. #1076 and #1078 also edit ingress.rs. This pull request's own commit does not.

#1080 and #1081 are CLI targets and access / whoami. #1059 is --as. #1012 is the branch listing. Draft #675 (RFC 0059) is still a draft. This library takes a root, not a named target. Findings for those belong on those pull requests.

Tradeoff and liability

One serving projection and one release function is the right shape. A second cluster server would add a second contract to keep true. The merge kept that function. LocalServing::serve calls it. The cluster lifecycle test shows a clean shutdown removes the lock file.

AppState::local for a cluster is a new public door. The second caller drops three facts the deployment keeps: the boot witness, the all-failed refusal, and a policy when the bundle is absent. Five more local scopes would copy those omissions if each scope builds its own startup. Use the deployment checks. A separate startup type for local servers would fork the contract again on the next scope.

Read-write holds a lock that Drop does not release. An embedder that drops the state, that hits an error after admission, or that drops LocalServing before serve, leaves __cluster/lock.json in place. A deployment process exits. This caller stays alive. After the merge, a drop before serve also leaves the signal handlers installed, so SIGTERM does not end the process. That is new operational liability on top of the admission contract. The contract itself is right: Drop must not unlock after a write.

A read-only server takes no admission, so cluster apply can run beside it. Policies, stored queries, embedding bindings, and Blob rules stay at the captured revision. Graph handles still follow the branch head. Report the booted revision on /readyz so an operator can see which ledger those policies came from.

The forbid-on-loaded-policy design is right when a bundle exists. The tests prove that case. A cluster with no bundle is a supported deployment mode, and this path skips it.

We keep one startup path and accept a slightly larger change in this pull request. A smaller patch that leaves the three omissions in place costs more on the next scope.

Validation

Head c04a0c850ce155762ddc005352b4baede466bf87. Parent 6a4b02cffd (#1075). The previous review head was cc68d0c32472e42c536604d98484644894814cc1. Base 0a62bb7b2aa5b99b012f413807c216277f7f9596.

Commands from this checkout, with CARGO_TARGET_DIR=/workspace/target, RUST_MIN_STACK=16777216, and --locked:

  • cargo test -p omnigraph-policy --lib tests::a_forbidden_action_is_denied_whatever_permits_it -- --exact — 1 passed, exit 0
  • cargo test -p omnigraph-server --lib local:: — 3 passed, exit 0
  • cargo test -p omnigraph-server --test integration local_cluster — 2 passed, exit 0, after the probe was removed
  • cargo test -p omnigraph-server --test boot_settings local_serving:: — 5 passed, 2 ignored (instrument: helpers), exit 0. The cluster case released the lock on a clean shutdown. The uncertain case is a standalone graph.

CI on this head is green: CI Gate run 39173259917 (Test Workspace, clippy, rustfmt, server aws, S3, Azure), GQT run 39173259881, DST pinned suite run 39173259784. Devin Review skipped a full review (trial expired). There is no other human review on this head.

This pull request is a feature. GQT cannot express admission, process lifecycle, /readyz, or an engine write on a handle. I am not asking for a .gqt case. The three blocking contracts need assertions on the auth_policy owner.

A temporary probe, auth_policy::temp_probe_local_cluster_contract, ran in this checkout and was removed. It was not pushed. At this head it failed for the predicted reasons:

  • Ledger state_revision was 5. Read-only and read-write /readyz were both 200 with state_revision 0 and no CAS or digest.
  • After deleting graphs/knowledge.omni, read-only local() returned Ok. /readyz was 503 blocked.
  • A policy-less cluster had policy.is_none() == true. branch_create_as returned Ok(()). Mutate returned HTTP 403 with the read-only message.

I did not run that probe on parent 16de8b9d. LocalScope::Cluster is not on that commit. In this same tree, serve_config installs the witness and finish_graph_startup refuses an all-failed open. The local door does not call those paths.

Limitations

I did not rerun the full workspace suite locally. CI did, on this head. I did not run this path against S3 or Azure. I did not re-read Lance storage pages. This commit does not change Lance behavior. I checked open_read_only, enforce, and ClusterAdmission drop in OmniGraph source. OpenMode::ReadOnly skips open-time writes only. The uncertain-completion test in boot_settings uses a standalone graph, so I did not execute an uncertain cluster completion. The release branch is the same function a deployment uses. The announce-window signal test covers a standalone graph only.

A write whose outcome is unknown still stops the server that ran it, so
`AppState::local` refuses `LocalAccess::ReadWrite` with
`LocalOpenError::ReadWriteUnavailable` until it no longer does. The
read-write path stays and stays tested: the `test-util` feature's
`with_local_read_write` scope opens it for tests alone, enabled from
dev-dependencies only (this crate's own included) and checked by the
test-util guard, and a build with the feature still refuses outside the
scope.

`LocalBinding::local(path)` replaces the string constructor. It takes a
root on this machine's file system, made absolute, and refuses a URI: a
remote root's endpoint and credentials would come from the process
environment when it opens, which a captured binding must not depend on.

A read-only local server's engine refuses a direct write, now tested on
the handle. The developer guide says what the read-write gate is, that
an embedder a browser can reach adds the loopback guard in `extend`, and
that Tokio keeps the signal handlers for the rest of the process. The
invariants say the library serves one standalone graph on loopback
while the binary stays cluster-only, and the release notes no longer
claim read-write or a wider signal window than the code covers.
…server

# Conflicts:
#	crates/omnigraph-server/src/local.rs
#	crates/omnigraph-server/tests/boot_settings.rs
#	docs/dev/control-plane.md
@ragnorc
ragnorc requested a review from azimafroozeh as a code owner October 19, 2026 00:03
@ragnorc

ragnorc commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Addressed at d3170d6: fix 00ce414, plus a merge of #1075's 4708c68.

Blocking

  1. /readyz. Both accesses install the snapshot's boot witness. The auth_policy local-cluster tests assert the revision, CAS and serving digest.
  2. All graphs failed. Read-only refuses when no graph opens and lists each graph's failure; one blocked graph beside a ready one still serves. A ledger revision that moves between capture and validate_opened now refuses the whole server; applied_schema_drift still blocks only its graph. The control-plane page lists what refuses and what blocks. Tests: a_local_cluster_server_refuses_when_no_graph_opens and a_read_only_open_refuses_a_moved_revision_and_blocks_a_drifted_graph.
  3. No policy bundle. A graph without one gets a generated policy granting the actor read on every branch, with the write forbid on top for read-only. It is installed on the engine too. Test: a_local_cluster_server_grants_only_read_on_a_graph_without_a_policy checks that engine mutate_as fails with OmniError::Policy while HTTP reads still work.

Optional

  1. Refusals before any graph opens release the admission. Once a graph has opened, a failed startup keeps it and returns LocalOpenError::AdmissionRetained { lock_id }.
  2. require_all_graphs now comes from config.
  3. The catalogue policy passes through the write forbid.
  4. Drop. LocalServing::release ends a server that never served and releases its admission after a clean drain. The page now says that dropping an unserved LocalServing keeps the admission.

Policy ids are namespaced (permit:/forbid:), so a rule named local-read-only no longer collides. Relative roots are made absolute and file:// URIs are built with Url::from_file_path.

With #1075's gate, read-write cluster serving is refused too: ReadWriteUnavailable, open only inside the test scope. The changelog says read-only. Not fixed here: ClusterStore::for_config_dir builds file://{path} unencoded, so a cluster root containing # or % misbehaves in cluster apply itself.

ragnorc commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

This pull request serves a cluster's applied state on loopback to the same OS account. An embedder then uses the cluster's policies, stored queries, and bindings.

LocalScope::Cluster opens that applied state. Read-only capture takes no cluster lock. It opens each graph against one ledger revision and forbids every write in each policy. Read-write takes the serving admission a deployment takes. The public API refuses read-write until a write with an unknown outcome no longer stops the server.

A client can read /readyz and see the booted revision. An embedder cannot write through a handle when the cluster binds no policy. The support-boundary text still names only a standalone graph.

Request changes.

Reviewed head d3170d62f648962c47a9d420fd8fd4107333576a. The review surface is that commit's difference from parent 4708c683 (#1075), including the conflict resolution in the merge. The author's note is #1077 (comment).

Earlier findings

Earlier finding At d3170d62
Blocking. /readyz reports ledger revision 0. From the review at cc68d0c3, still open at c04a0c85. Resolved. local_cluster installs the snapshot witness on both accesses. The auth_policy tests assert the revision, the CAS, and the serving digest.
Blocking. A read-only server returns Ok when every graph fails to open. Resolved. Read-only returns LocalOpenError::Serving when no graph opens, and when require_all_graphs is set and any graph fails. a_local_cluster_server_refuses_when_no_graph_opens deletes graphs/knowledge.omni and expects that error.
Blocking. A cluster with no policy bundle leaves the engine open to writes. Resolved. A graph with no bundle receives a generated policy that grants read on every branch. The engine installs that policy. a_local_cluster_server_grants_only_read_on_a_graph_without_a_policy expects OmniError::Policy from mutate_as in both modes, and HTTP query 200.
Optional. Errors after admission, and before a clean serve, drop the lock and name no lock id. Partly resolved. A refusal before any graph open calls release_settings_refusal. After an open, the error is AdmissionRetained and names the lock. The test checks that the lock file contains that id. start_local still drops the state when the listener is not loopback, and that drop keeps a lock the error does not name. AppState::local refuses ReadWrite before admission outside the test scope, so a production caller cannot reach that window. No further change requested.
Optional. OMNIGRAPH_REQUIRE_ALL_GRAPHS can refuse settings and still allow a partial open. Resolved. Both arms pass settings.require_all_graphs. No new test sets the variable.
Optional. The catalogue policy does not pass through forbid_writes. Resolved. The read-only arm runs forbid_writes on the catalogue policy.
Optional. Dropping LocalServing before serve keeps the cluster lock. Resolved. LocalServing::release drains and releases the admission after a clean drain. release_stops_an_unserved_local_server_and_frees_its_cluster shows the lock file is gone and the next owner is admitted. The control-plane page says a drop keeps the admission. Drop does not unlock. The lock id is not a field on LocalServing. The next state_lock_held refusal names the holder.

Blocking

1. The support boundary names only a standalone graph

docs/dev/invariants.md says AppState::local and serve_local serve one standalone graph read-only on loopback and take no cluster admission.

This pull request also serves one cluster read-only on loopback. That path takes no admission. The sentence is false. The developer guide is the boundary the next change applies.

Write the boundary this branch implements. The library serves one standalone graph or one cluster, read-only, on loopback. A read-only open takes no cluster admission. Read-write stays refused until a write with an unknown outcome no longer stops the server.

The draft RFC says this sentence should name both scopes. The control-plane page already does.

docs/dev/invariants.md lines 166–168. Parent 4708c683 added the standalone sentence. This head does not change that file.

Inline, docs/dev/invariants.md:168: The support boundary says AppState::local and serve_local serve one standalone graph read-only on loopback and take no cluster admission. This pull request also serves one cluster read-only on loopback. That path takes no admission. The sentence is false. Write the boundary this branch implements. The library serves one standalone graph or one cluster, read-only, on loopback. A read-only open takes no cluster admission. Read-write stays refused until a write with an unknown outcome no longer stops the server (issue #1067). The draft RFC (PR #1017) says this sentence should name both scopes.

Optional

1. The new text says /graphs reports the ledger revision

GET /graphs returns GraphListResponse { graphs } only. It has no ledger revision, no CAS, and no serving digest. GET /readyz reports those three fields. The new tests assert /readyz.

server_graphs_list is the same handler a deployment uses. A deployment's /graphs omits the same fields.

Delete "and /graphs" from the new control-plane paragraph and from the comment above with_boot_witness in local.rs.

docs/dev/control-plane.md line 569. crates/omnigraph-server/src/local.rs line 462. crates/omnigraph-server/src/handlers.rs server_graphs_list.

Inline, docs/dev/control-plane.md:569: GET /graphs returns the graph list only. It has no ledger revision, no CAS, and no serving digest. GET /readyz reports those three fields, and the new tests assert them. server_graphs_list builds GraphListResponse { graphs }. A deployment's /graphs omits the same fields. Delete "and /graphs" from this sentence and from the comment above with_boot_witness in crates/omnigraph-server/src/local.rs.

Inline, crates/omnigraph-server/src/local.rs:462: Same claim as the control-plane sentence. GET /graphs does not report the revision. GET /readyz does. Delete "and /graphs".

Where the code and the RFC differ

RFC head 7c72c7e7ed2b42fb0e5b3f5140d8235444ddcf33 (PR #1017, still open, still draft). Issue #1067 is still open.

The previous disagreement on rollout phase 4 is closed. Phase 3 is read-only local servers. Phase 4 waits on #1067. AppState::local returns ReadWriteUnavailable unless the caller is inside with_local_read_write, and that scope exists only for tests. The changelog says the same. Merge still waits on acceptance of the RFC, as the pull request says.

These differences remain. Update the RFC when it is accepted. I am not asking for the sample signatures to be copied into this pull request.

  • "A local state is never in open or default-deny mode." Each graph engine now has a policy, including a graph the cluster binds no bundle to. A cluster with no server bundle still denies graph_list through the deployment default-deny path. That is what the routes do. The engine sentence holds.
  • RFC step 1 says a graph root inside a cluster becomes LocalScope::Cluster. The code returns LocalOpenError::ClusterGraph and requires LocalScope::Cluster. Keep the explicit scope. An automatic promotion would widen a graph open into a cluster open.
  • LocalBinding is an absolute path on this machine. The RFC sample carries storage-provider settings and is never a bare URI. The path type comes from feat(server): serve one standalone graph to the same account on loopback #1075. This pull request uses it for clusters. cluster_storage_uri still has S3 and Azure arms. LocalBinding::local refuses a URI, so those arms do not run. Remote roots wait. ReadWriteUnavailable, AdmissionRetained, start_local, and LocalServing::release are the names to add. The RFC says the implementation pull request settles the signatures.
  • Read-only capture keeps one ledger revision for the life of the process. A later apply is visible at the next start. Graph handles still follow the branch head. /readyz now names the captured revision.
  • The Azure clean-exit path that retains admission does not run here. The binding refuses a remote URI.

Sibling pull requests

Merge after #1075. This head contains #1075 at 4708c683.

Own delta from that parent: changelog.d/local-cluster-server.added.md, crates/omnigraph-policy/src/lib.rs, crates/omnigraph-server/src/{deployment.rs,lib.rs,local.rs,settings.rs}, tests/{boot_settings.rs,integration/auth_policy.rs,support/mod.rs}, docs/dev/control-plane.md. This commit does not edit ingress.rs.

Current sibling heads, all still open:

deployment.rs in this pull request only passes GraphOpenAccess::Writable into prepare_single_graph. That will conflict with #1076, #1078, and #1080 in text, and the change is mechanical. CLI targets, access / whoami, --as, and the branch listing belong on those pull requests. This library takes a storage root, not a named target.

Tradeoff and liability

One serving projection and one release function is the right shape. The three omissions from the earlier reviews are closed by the deployment checks: the boot witness, the all-failed refusal, and a policy when the bundle is absent. A second startup for local servers would fork that contract on the next scope.

Public read-write is refused. That removes the liability of a long-lived process holding a lock that Drop does not release, until #1067. The read-write path remains behind the test scope, so the admission contract stays tested. LocalServing::serve releases the lock after a clean drain and keeps it when the outcome is uncertain. That matches a deployment.

AppState::local for a cluster is a new public door. Five more local scopes should call this witness, this all-failed check, and this no-bundle policy. Each scope that builds its own startup will omit one of them again.

Cedar permit ids now use a permit: prefix, and forbids use forbid:. A forbid can share a rule id with a permit. matched_rule_id stays the rule id. The policy tests passed, and the cluster test reports local-read-only for a denied change while the cluster rule id is local-data.

The false support-boundary sentence is new liability. The next change will treat cluster local serving as outside the published boundary.

We keep one startup path and accept the doc fix in this pull request. Shipping read-write now would be the wrong priority while #1067 is open. Sequential read-only opens keep one revision fail-closed. The deployment opens graphs concurrently. The sequential open costs latency and keeps the contract.

Validation

Head d3170d62f648962c47a9d420fd8fd4107333576a. Parent 4708c6833433. The previous review head was c04a0c850ce155762ddc005352b4baede466bf87. Base 0a62bb7b2aa5b99b012f413807c216277f7f9596.

Commands from an isolated checkout at this head, with CARGO_TARGET_DIR=/workspace/target, RUST_MIN_STACK=16777216, and --locked:

  • cargo test -p omnigraph-policy --lib — 21 passed, exit 0. Includes tests::a_forbidden_action_is_denied_whatever_permits_it.
  • cargo test -p omnigraph-server --lib local:: — 8 passed, exit 0. Includes a_read_only_open_refuses_a_moved_revision_and_blocks_a_drifted_graph.
  • cargo test -p omnigraph-server --test integration local_ — 8 passed, exit 0. Includes the three cluster contracts and the read-write admission test.
  • cargo test -p omnigraph-server --test boot_settings local_serving:: — 6 passed, 2 ignored (instrument: helpers), exit 0. The cluster case released the lock on a clean shutdown. release_stops_an_unserved_local_server_and_frees_its_cluster passed.

CI on this head is green. CI Gate run 39179055754, GQ Logic Tests run 39179055505, DST pinned suite run 39179055621. Test Workspace, clippy, rustfmt, server aws, S3, and Azure completed with success. Devin Review is success on the commit status and records no review body. There is no other human review on this head.

These fixes are process and engine contracts. GQT cannot express admission, process lifecycle, /readyz, or an engine write on a handle. I am not asking for a .gqt case. The pull request already has the Rust regressions on the auth_policy owner.

A temporary probe ran at c04a0c85 and was removed. It was not pushed. The three tests assert the fixed contract. At that commit they failed for the predicted reasons, exit 101:

  • Ledger state_revision was 5. Read-only /readyz was 200 with state_revision 0. The body has no state_cas and no booted_serving_digest. The assertion stopped on that access. Both accesses used the same tail, and that tail did not install the witness. The head tests assert both accesses.
  • After deleting graphs/knowledge.omni, read-only local() returned Ok.
  • A policy-less cluster had no engine policy. branch_create_as returned Ok(()).

At this head the three auth_policy tests that replace that probe passed.

Limitations

I did not rerun the full workspace suite locally. CI did, on this head. I did not run this path on S3 or Azure. The binding refuses those URIs. I read the Lance object-store page. A file root is local storage. This commit does not change Lance dataset writes. The cluster lock is __cluster/lock.json.

I did not re-review the #1075 read-write gate, the signal-handler registration, or the Host guard, except where a cluster admission meets them. The uncertain-completion test uses a standalone graph. The announce-window signal test uses a standalone graph. The cluster lifecycle test covers a clean shutdown and LocalServing::release.

The support boundary in the invariants named only a standalone graph,
though this change also serves one cluster read-only on loopback without
cluster admission; it now names both scopes and that read-write stays
refused until a write whose outcome is unknown no longer stops the server.
The control-plane page and the comment above the boot witness said
`/graphs` reports the ledger revision; only `/readyz` does.
@ragnorc

ragnorc commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Addressed at 1413287.

Blocking

  1. Support boundary. docs/dev/invariants.md now says the library serves one standalone graph or one cluster read-only on loopback. A read-only open takes no cluster admission. Read-write stays refused until a write whose outcome is unknown no longer stops the server.

Optional

  1. "and /graphs" is gone from the control-plane paragraph and from the comment above with_boot_witness; only /readyz reports the served revision.

RFC differences: #1017's local embedding API now matches this branch.

  • LocalBinding::local takes local roots only, and remote roots wait for a binding that carries the resolver's storage settings.
  • It names ReadWriteUnavailable, ClusterGraph, AdmissionRetained, start_local and LocalServing::release.
  • A graph inside a cluster is refused rather than promoted.
  • A read-only cluster server captures one ledger revision for its life, a graph without a bundle gets a read-only policy on its engine too, and /readyz reports the captured revision.

…-write

A read-only local server refused every write with "start it with --write
to change the graph", though `AppState::local` refuses read-write outside
the test scope and no command accepts `--write` yet. The refusal now says
the server is read-only and that a read-write local server is not
available yet, and the route test checks that it names no `--write`.

`LocalBinding::local` folds `.` and `..` with the storage crate's lexical
absolutizer, now re-exported beside `normalize_root_uri`, so one
directory has one stored spelling. The embedding page no longer cites a
command this change does not ship.
Brings in the read-only refusal that names no --write, the folded local
binding root, and the embedding page without the serve reference.
Brings in the access route from ModernRelay#1073; its tests and the local server's
sit side by side at the end of auth_policy.rs.
Brings in main through ModernRelay#1075: the access route from ModernRelay#1073, whose tests
follow the local cluster server's at the end of auth_policy.rs.
ragnorc added a commit to ragnorc/omnigraph that referenced this pull request Oct 19, 2026
Brings in ModernRelay#1075's fix through ModernRelay#1077: the read-only refusal names no
--write, and a local binding folds `..` before it is stored.
The engine re-exports the storage crate's lexical absolutizer for the
local server's binding; the split-crate re-export guard lists every such
item by name, so it now names this one.
Brings in the engine's pinned re-export of absolutize_lexically.

ragnorc commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

This pull request serves one cluster on loopback to the same OS account. An embedder then uses the cluster's policies, stored queries, and bindings.

LocalScope::Cluster opens the applied state. Read-only capture takes no cluster lock. It opens each graph against one ledger revision and forbids every write in each policy. Read-write takes the serving admission a deployment takes. The public API refuses read-write until a write with an unknown outcome no longer stops the server.

The commits after d3170d62 correct the support boundary and the /readyz wording, and they merge the updated parent branch.

Approve.

Reviewed head b296337bc10fc5cb6005f15ab4e1ddc8d078d591. The review surface is d3170d62..b296337b: commit 1413287c, plus the merges of feat/local-server-foundations. The author's note is #1077 (comment).

Earlier findings

Earlier finding At b296337b
Blocking. The support boundary names only a standalone graph. From the review at d3170d62. Resolved. docs/dev/invariants.md says the library serves one standalone graph or one cluster, read-only, on loopback. A read-only open takes no cluster admission. Read-write stays refused until a write whose outcome is unknown no longer stops the server.
Optional. The new text says /graphs reports the ledger revision. Resolved. The control-plane paragraph and the comment above with_boot_witness name only /readyz. GraphListResponse is { graphs }. ReadinessOutput carries the revision, the CAS, and the serving digest.

Blocking

None.

Optional

None.

Delta

1413287c is the documentation fix. Three merges follow it.

a30b2175 conflicted in docs/dev/control-plane.md. The result keeps this pull request's LocalServing::release sentences. It drops as omnigraph serve does. The parent removed that phrase. omnigraph serve is #1083.

f8685ecb conflicted at the end of tests/integration/auth_policy.rs. The result keeps the cluster tests and then the access-route tests from #1073. auth_policy::access_reports_the_callers_own_actions_as_routes_decide_them passed.

b296337b merges c4f844cb with an empty remerge diff. That parent commit pins absolutize_lexically. Cluster opens go through LocalBinding::local, so they use the same fold.

Where the code and the RFC differ

RFC head e15d2f5b85a759b053dda73082a5c9e7fff8e121 (PR #1017, still open, still draft). Issue #1067 is still open.

The 2026-10-19 RFC note matches this branch on the points the last review left open:

  • Phase 3 is read-only local servers. Phase 4 waits on bug: one storage write with an unknown outcome stops the server until a manual unlock #1067. AppState::local returns ReadWriteUnavailable outside with_local_read_write. The changelog says the same.
  • A graph root inside a cluster returns LocalOpenError::ClusterGraph. The caller passes LocalScope::Cluster.
  • LocalBinding::local takes a path on this machine and refuses a URI. Remote roots wait.
  • A read-only cluster server captures one ledger revision for its life. /readyz reports that revision. A graph with no bundle gets a policy that grants read, on the engine as well. The server refuses when no graph opens.
  • The support boundary names both scopes.

One sample comment still differs. The RFC says LocalOptions.actor is required for ReadWrite, and that a missing actor becomes default for a read-only graph. The code also requires an actor for every cluster open, including read-only (ActorRequired in local_cluster). The code comment and the control-plane page already say that. Add the same sentence to the RFC sample before #1017 is accepted. This pull request needs no code change for it. The RFC says the implementation pull request settles the signatures.

A cluster with no server bundle still denies graph_list when no server policy is installed. A deployment does the same. The engine on each graph has a policy. The sentence "a local state is never in open or default-deny mode" holds for those engines.

Sibling pull requests

Merge after #1075. This head contains #1075 at c4f844cb.

#1073 is merged. This head includes its access route through the parent merge of main. The access route evaluates the installed graph policy. A read-only cluster server has already run forbid_writes on that policy, so a write action is absent from the answer.

Current sibling heads, all still open unless noted:

deployment.rs in this pull request passes GraphOpenAccess::Writable into prepare_single_graph. That will conflict in text with #1076, #1078, #1080, and #1081. The resolution is mechanical. CLI targets, access / whoami, --as, and the branch listing belong on those pull requests. This library takes a storage root, not a named target.

Tradeoff and liability

One serving projection and one release function is the right shape. The support boundary now matches the code. The next change will treat a cluster local server as inside the published boundary.

Public read-write stays refused. That keeps a long-lived process from holding a lock that Drop does not release, until #1067. The read-write path stays tested behind the test scope. LocalServing::serve releases the lock after a clean drain and keeps it when the outcome is uncertain. That matches a deployment.

A read-only server takes no admission, so cluster apply can run beside it. Policies, stored queries, embedding bindings, and Blob rules stay at the captured revision. Graph handles still follow the branch head. /readyz names the captured revision.

The doc fix is the small change. Five more local scopes should keep this witness, this all-failed check, and this no-bundle policy. The sequential read-only open keeps one revision fail-closed. A deployment opens graphs concurrently. The extra latency is the cost of that check.

Validation

Head b296337bc10fc5cb6005f15ab4e1ddc8d078d591. Parent c4f844cb17002518c962000622c6f21e14a84f23. The previous review head was d3170d62f648962c47a9d420fd8fd4107333576a.

Commands from an isolated checkout at this head, with CARGO_TARGET_DIR=/workspace/target, RUST_MIN_STACK=16777216, and --locked:

  • cargo test -p omnigraph-policy --lib — 21 passed, exit 0. Includes tests::a_forbidden_action_is_denied_whatever_permits_it.
  • cargo test -p omnigraph-server --lib local:: — 8 passed, exit 0. Includes a_read_only_open_refuses_a_moved_revision_and_blocks_a_drifted_graph.
  • cargo test -p omnigraph-server --test integration local_ — 8 passed, exit 0. Includes the cluster contracts and the read-write admission test.
  • cargo test -p omnigraph-server --test integration auth_policy::access_reports_the_callers_own_actions_as_routes_decide_them -- --exact — 1 passed, exit 0. This is the test the f8685ecb conflict appended.
  • cargo test -p omnigraph-server --test boot_settings local_serving:: — 6 passed, 2 ignored (instrument: helpers), exit 0. The cluster case released the lock on a clean shutdown. release_stops_an_unserved_local_server_and_frees_its_cluster passed.

CI on this head: run 39277627199 passed Test Workspace, clippy, rustfmt, server aws, S3, and Azure. GQT ordinary and GQT served passed (run 39277626939). GQT (dst) failed case::load_csv_integer_endpoint_memory.gqt with case exceeded wall-time budget 10s. That case is not in this commit. Main added it in #1085 and shrank it in #1087 (9e2425d2). The GQ Logic Tests job failed because that qualification failed. The CI Gate job was cancelled. Devin Review records no review body (trial expired). There is no other review on this head.

This delta changes documentation. GQT cannot express the support-boundary sentence, or which route reports the ledger revision. I am not asking for a .gqt case. The behavior fixes from d3170d62 are unchanged apart from the comment above with_boot_witness. Their auth_policy tests passed again at this head.

Limitations

I did not rerun the full workspace suite locally. CI did, on this head, apart from the one GQT case above. I did not run this path on S3 or Azure. The binding refuses those URIs. A fetch of the Lance object-store page returned no text. This delta does not change Lance dataset writes. The cluster lock is __cluster/lock.json.

I did not re-review the #1075 read-write gate or the signal-handler registration, except where these merges meet this pull request's files. The uncertain-completion test uses a standalone graph. The cluster lifecycle test covers a clean shutdown and LocalServing::release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants