FlureeLabs

Override Control

Fluree's config resolution follows a three-tier precedence model. Each setting group is resolved independently, and an override control mechanism governs whether higher-priority sources can change values set at lower tiers.

Resolution precedence

Settings are resolved from lowest to highest priority:

PrioritySourceWhen it applies
4 (lowest)System defaultsNo config present (allow-all, no SHACL, no reasoning)
3Ledger-wide config (f:LedgerConfig)Fallback for any setting not overridden at higher tiers
2Per-graph config (f:GraphConfig)Only if ledger-wide override control permits
1 (highest)Query/transaction-time optsOnly if effective override control permits + identity check passes

Override control modes

Each setting group may include an f:overrideControl field controlling whether higher-priority sources can override the value.

ModeValueBehavior
No overridesf:OverrideNoneConfig values are final. No per-graph or query-time overrides permitted.
All overridesf:OverrideAllAny request can override. Default when f:overrideControl is absent.
Identity-gatedObject with f:controlMode: f:IdentityRestrictedOnly requests with a server-verified identity matching f:allowedIdentities can override.

Identity-gated example

{
  "f:overrideControl": {
    "f:controlMode": { "@id": "f:IdentityRestricted" },
    "f:allowedIdentities": [
      { "@id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK" }
    ]
  }
}

Identity verification

Override identity is the server-verified request identity (canonical DID string), not a user-supplied query parameter. Specifically:

  • With the credential feature: the DID from the verified JWS kid header
  • With server auth middleware: the DID mapped from an OAuth token
  • A caller cannot become an allowed identity by setting "opts": {"identity": "..."} in query JSON — that field is for policy evaluation context, not override authorization
  • Anonymous requests (no verified identity) are always denied by f:IdentityRestricted

Query-time vs transact-time overrides

  • Query-time overrides (reasoning modes, policy opts): identity is the query caller
  • Transact-time overrides (SHACL mode, validation settings): identity is the transaction signer

Monotonicity: per-graph can only tighten

Ledger-wide f:overrideControl sets the maximum permissiveness. Per-graph configs may only restrict further, never loosen.

Permissiveness ordering: f:OverrideNone < f:IdentityRestricted < f:OverrideAll

The effective per-graph override control is min(ledger-wide, per-graph):

Ledger-widePer-graphEffectiveWhy
OverrideNoneOverrideAllOverrideNonePer-graph cannot loosen (warning logged)
IdentityRestricted({alice})OverrideAllIdentityRestricted({alice})Per-graph cannot loosen
IdentityRestricted({alice, bob})IdentityRestricted({alice})IdentityRestricted({alice})Intersection: per-graph tightens
OverrideAllOverrideNoneOverrideNonePer-graph tightens (valid)
OverrideAllIdentityRestricted({alice})IdentityRestricted({alice})Per-graph tightens (valid)
OverrideAll(absent)OverrideAllInherits ledger-wide

When both are IdentityRestricted, the effective allowedIdentities is the intersection of the two lists.

Resolution algorithm

For each setting group independently:

1. Start with system defaults
2. Apply ledger-wide config for this group (if present)
3. Get ledger-wide overrideControl (default: OverrideAll)
4. If ledger-wide overrideControl is OverrideNone:
     → this group is final. Skip to step 8.
5. Apply per-graph config for this group (if present)
6. Compute effective overrideControl:
     = min(ledgerWide, perGraph)
     If both IdentityRestricted: allowedIdentities = intersection
7. Check effective overrideControl against query/txn-time opts:
     OverrideNone         → config values are final
     OverrideAll          → apply query-time opts
     IdentityRestricted   → apply only if request identity matches
8. Result is the effective setting for this group.

Per-group truth tables

Policy (f:policyDefaults)

Ledger-widePer-graphQuery (identity)EffectiveWhy
defaultAllow: false, OverrideNone(none)defaultAllow: true (any)denyNo overrides allowed
defaultAllow: false, OverrideAll(none)defaultAllow: true (any)allowAll overrides allowed
defaultAllow: false, IdentityRestricted({alice})(none)defaultAllow: true (alice)allowAlice is authorized
defaultAllow: false, IdentityRestricted({alice})(none)defaultAllow: true (bob)denyBob not authorized
defaultAllow: false, IdentityRestricted({alice})(none)defaultAllow: true (anon)denyNo identity = no override
defaultAllow: false, OverrideNonedefaultAllow: true(none)denyOverrideNone blocks per-graph
defaultAllow: false, OverrideAlldefaultAllow: true(none)allowPer-graph overrides ledger-wide
defaultAllow: true, OverrideAlldefaultAllow: false, OverrideNonedefaultAllow: true (any)denyPer-graph OverrideNone blocks query
(none)(none)(none)allowSystem default (allow-all)

Reasoning (f:reasoningDefaults)

Ledger-widePer-graphQuery (identity)EffectiveWhy
modes: [rdfs], OverrideNone(none)reasoning: [owl2-rl] (any)rdfsNo overrides
modes: [rdfs], OverrideAll(none)reasoning: [owl2-rl] (any)owl2-rlOverride allowed
modes: [rdfs], IdentityRestricted({alice})(none)reasoning: [owl2-rl] (alice)owl2-rlAlice authorized
modes: [rdfs], IdentityRestricted({alice})(none)reasoning: [owl2-rl] (bob)rdfsBob not authorized
modes: [rdfs], OverrideAllmodes: [owl2-rl](none)owl2-rlPer-graph overrides
modes: [rdfs], OverrideNonemodes: [owl2-rl](none)rdfsOverrideNone blocks per-graph

SHACL (f:shaclDefaults)

Ledger-widePer-graphEffectiveWhy
enabled: false, OverrideNoneenabled: truedisabledOverrideNone blocks per-graph
enabled: true, OverrideAllenabled: falsedisabledPer-graph disables for its graph
mode: warn, OverrideAllmode: rejectrejectPer-graph overrides

Transact (f:transactDefaults)

Transact defaults use additive merge semantics, unlike other groups. However, the general override control rule still applies: if the ledger-wide f:overrideControl is f:OverrideNone, per-graph transact defaults are blocked entirely.

Ledger-widePer-graphEffectiveWhy
uniqueEnabled: trueuniqueEnabled: falseenabledMonotonic OR — cannot disable
uniqueEnabled: true, sources: [default]sources: [schemaGraph]sources: [default, schemaGraph]Additive — sources accumulate
uniqueEnabled: falseuniqueEnabled: trueenabledPer-graph can enable
uniqueEnabled: true, OverrideNonesources: [schemaGraph]sources: [default] onlyOverrideNone blocks per-graph additions

Overridable vs non-overridable fields

Not all fields in a setting group are overridable. Source pointers (where rules/shapes/schema come from) are always config-only:

SubsystemOverridable fieldsNon-overridable (config-only)
f:policyDefaultsf:defaultAllow, f:policyClassf:policySource
f:shaclDefaultsf:validationMode, f:shaclEnabledf:shapesSource
f:reasoningDefaultsf:reasoningModesf:schemaSource
f:datalogDefaultsf:datalogEnabled, f:allowQueryTimeRulesf:rulesSource

Non-overridable fields can only be changed by writing to the config graph. This prevents a query from redirecting the engine to read rules or schema from an arbitrary graph.

Per-graph overrides

Per-graph overrides target specific named graphs by IRI:

@prefix f: <https://ns.flur.ee/db#> .

GRAPH <urn:fluree:mydb:main#config> {
  <urn:fluree:mydb:main:config:ledger> a f:LedgerConfig ;
    f:policyDefaults [
      f:defaultAllow true ;
      f:overrideControl f:OverrideAll
    ] ;
    f:graphOverrides (
      [ a f:GraphConfig ;
        f:targetGraph <http://example.org/sensitive> ;
        f:policyDefaults [
          f:defaultAllow false ;
          f:overrideControl f:OverrideNone
        ]
      ]
    ) .
}

In this example:

  • All graphs default to defaultAllow: true with OverrideAll
  • http://example.org/sensitive overrides to defaultAllow: false with OverrideNone — no query can override policy for this graph
  • f:targetGraph uses f:defaultGraph for the default graph