Skip to content

cluster apply: allow changing policy bundle content (group membership) on existing graphs; 0.12.0 refuses it with deployment_scope #882

Closed

Description

@feoh

Problem

In 0.12.0 the content of an applied policy bundle, including its groups: membership, cannot be changed once the cluster ledger has any resources. Both direct apply and apply --server refuse it with deployment_scope, and a fresh ledger cannot adopt existing graphs, so there is no supported way to admit a new actor to an existing graph. 0.11 allowed this (Update policy.<name> [Applied]).

This breaks a common deployment shape: Cedar groups whose membership tracks an identity directory. Ours has one omnigraph actor per human in a realm, and users are added over time. Under 0.12.0 a user added after a graph is created can never be granted access to it. docs/user/operations/policy.md and server.md say existing policy bindings stay fixed in the "current deployment class", but the only way to add an actor under that rule is to rebuild the graph.

Policy rules have no wildcard or "any authenticated actor" selector (PolicyActorSelector has only group), so the membership list cannot be avoided by writing the rule differently.

Reproduction (0.12.0, linux-x86_64, local scratch cluster)

  1. A cluster.yaml with one graph memory, a cluster-scoped bundle granting config_manage and graph_list, and a graph bundle memory_rules with groups: { users: [act-alice] }.
  2. omnigraph cluster apply --config . --as act-deployer, then omnigraph cluster force-unlock <id>.
  3. Add act-bob to users: in the graph bundle and run any of:
    • omnigraph cluster apply --config . --as act-deployer (server stopped, lock released)
    • omnigraph cluster apply --server http://127.0.0.1:18082 --config . (server running, caller holds config_manage)

Both fail:

ERROR deployment_scope __cluster/state.json: policy.memory_rules changes unsupported graph removal or existing runtime bindings

The server form reports "code": "conflict" with the same message.

  1. Deleting __cluster/ and re-running apply to start a fresh ledger fails with graph_already_exists ... bootstrap never adopts an unregistered graph.

On 0.11.0 the same edit after cluster refresh applies cleanly: Update policy.memory_rules [Applied].

The refusal comes from preview_deployment_scope in crates/omnigraph-cluster/src/deployment/execution.rs. A ResourceKind::Policy resource is accepted only when the ledger is empty, the policy is new, or old.digest == new.digest (only the applies_to list may grow, onto newly created graphs).

Proposed change

Permit a deployment to replace the content of an existing policy bundle, authorized by config_manage on the current applied policy, as docs/rfcs/2026-09-09-identity-credentials-and-applied-policy.md already describes ("configuration administration, including policy/group changes"). Keep the existing safeguards: a proposed policy never authorizes itself, apply revalidates the base revision and CAS, and unchanged bytes are a no-op. A --server apply would swap the policy engine for the affected graphs inside the existing transition (the same drain used for schema changes).

If full policy replacement is too large a step, a narrower change that would still solve this is allowing only the groups: membership of an existing bundle to change while rules: and applies_to stay byte-identical.

Affected areas

  • crates/omnigraph-cluster/src/deployment/execution.rs (preview_deployment_scope)
  • cluster apply, direct and --server
  • docs/user/operations/policy.md, server.md, clusters/index.md, which describe bindings as fixed in the "current deployment class"
  • RFC 2026-09-09-identity-credentials-and-applied-policy

Alternatives considered

  • Pre-allocate a fixed pool of actor slots in each group at graph creation and map users to slots outside omnigraph. This works but weakens actor attribution in commit history and makes pool exhaustion a graph rebuild.
  • Use one shared actor for all humans. This loses per-actor attribution and revocation at the data tier.
  • Rebuild the graph (export, init, load) whenever membership changes. Not viable when membership changes daily.
  • Stay on 0.11 until this is supported.

Is a change to policy replacement planned, and if not, is there a supported pattern for changing group membership on existing graphs that I missed?

Activity

  1. feoh commented on Oct 6, 2026

    @feoh
    Author

    @blarghmatey for visibility.

  2. aaltshuler commented on Oct 7, 2026

    @aaltshuler
    Contributor

    Closing as fixed in 0.13 by #878.

    Existing graph policy content and group membership can now change through cluster apply. The direct deployment regression current_policy_authorizes_policy_edits_rebinding_and_removal covers adding an actor while preserving graph state. The real-server journey live_policy_grants_revocations_and_management_handoff covers live grant/revoke, current-policy authorization, management handoff and persistence after restart.

    Evidence: merged implementation, deployment tests, live CLI/server journey.

    Static bearer-token source reload is separate work tracked by #434.

  3. feoh commented on Oct 8, 2026

    @feoh
    Author

    Thanks very much for the fix - this unblocks our upgrade. Appreciate it!

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageAwaiting maintainer triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions