Arbiter

@arbiter/core (1.0.3)

Published 2026-08-03 19:06:41 +00:00 by Dvorak

Installation

@arbiter:registry=https://hub.kl1.tenere.ai/api/packages/Arbiter/npm/
npm install @arbiter/core@1.0.3
"@arbiter/core": "1.0.3"

About this package

@arbiter/core

Possibilistic authorization engine: graph indices, relation/reachability queries, rule evaluation over declarative configurations, and lossless condensed snapshots.

The Evidence DSL (a natural-language layer that compiles to these configurations) lives in the separate @arbiter/evidence-dsl package.

Why

Authorization policies live on a graph: users hold relations to objects, groups, and roles, and rules derive decisions from those relations. @arbiter/core answers one question — may user U perform relation R on object O? — with a possibility ranking, not a boolean. Callers supply evidence with strengths; the engine fuses it through rule operators (union, intersection, exclusion, defeasible, chain, multi-hop, relational comparator) and returns the strongest derivable possibility, the reliability of the decision, and the validity provenance of the ranking.

The engine does not police caller-supplied evidence: you supply validated relation strengths and proofs; the engine derives and fuses. It is a library, not a service — no storage, no transport, no policy source of truth.

Install

npm install @arbiter/core

The package is ESM-only and requires Node 22 or newer.

Quick Start

import { Arbiter } from '@arbiter/core';

const arbiter = new Arbiter();

// Nodes: an id and a type.
arbiter.addNode('user:1', 'user');
arbiter.addNode('doc:9', 'doc');

// Relations: a config says how decisions for that relation are derived.
arbiter.setRelationConfig('owner', { type: 'direct' });
arbiter.addRelation('user:1', 'owner', 'doc:9', { possibility: 0.9 });

// The core question.
const result = arbiter.check('user:1', 'owner', 'doc:9');
// { possibility: 0.9, reliability: 1,
//   validity: { label: 'heuristic', operator: 'identity', regime: 'arbitrary' },
//   reason: 'direct_match' }

Concepts

Possibility, not probability

Every check returns a possibility in [0, 1] — a maxitive ranking supplied by the caller through relation strengths. 1 means derivable, 0 means not derivable. Fusions take the maximum under union, and enforce thresholds and conflicts under intersection, exclusion, and defeasible operators.

Result shape

Every check result carries the same four fields:

Field Type Meaning
possibility number in [0,1] Derived possibility of the decision
reliability number in [0,1] Reliability of the decision; always 0 for denials
validity object Validity provenance: label, operator, regime (minimal form)
reason string Outcome class: direct_match, no_relation, threshold_not_met, missing_node, no_config, cycle, ...

Denied decisions never leak a source's reliability. includeMeta: true adds meta with the full provenance (allow/deny blocks, rule traces, thresholds) and the full validity block (sources, conflictMass, validifiedPossibility, nonMaxitive).

The caller owns evidence and time

  • Evidence: relation strengths and validity labels come from the caller. The engine derives and fuses but never judges.
  • Time: TTL-gated evidence uses the caller's clock. Pass { now } (or partialGraph.now) to pin the temporal context; a rerun with the same context reproduces the decision.

TTL is a value-freshness gate, not an access-expiry mechanism. valueManager.setTTL(relation, ms) controls how long a relation's value stays fresh for value-consuming paths (relational comparators, chain/multi-hop value collection): once age > TTL the value is treated as absent, which denies the comparator and drops the value from collected values. Possibility-based grants — a direct relation's allow/deny, union disjunction, chain traversal — are timeless: an edge grants regardless of its age. If you need access to expire, express it in the policy (e.g. a comparator over a time-carrying value), not via setTTL.

Clocks and caches. The decision caches (direct-check, rule-result, chain) are keyed on the meta-less decision form only: includeMeta callers and pinned-clock callers always get a fresh evaluation, and value-carrying results are never cached (values are TTL-gated evidence). Unpinned callers share wall-clock cache entries — the correct default for timeless decisions. Pin { now } whenever the answer depends on when you ask; every cache bypasses itself for pinned-clock callers, so a rerun with the same { now } reproduces the decision exactly.

Overlays and partial graphs

check accepts a PartialGraphContext overlay. Overlay relations take precedence over the base graph, letting you answer "what changes if this evidence appears?" without mutating the graph.

API

The full reference — every export, method signature, option, result shape, configuration format, error, and reason code — is in docs/API.md. The public surface at a glance:

  • Graph: addNode, addRelation, removeRelation, setRelationConfig, getNodeData, resolveNodeId, resolveKey
  • Check: check(userKey, relation, objectKey, options), explain (enriches meta), binary mode (fast path, marks results binary: true)
  • Reachability: isReachable, getReachableNodes, getReachingNodes, shortestPathLength, estimateGraphDistance (isReachable returns null when no PLTC index is available — a signal to defer to rule evaluation)
  • Snapshots: enableCondensedSnapshot, toSnapshotBinary, Arbiter.fromSnapshotBinary (lossless: carries relation metadata, TTLs, and validity; malformed buffers fail fast with clean errors)
  • Value context: valueManager (TTL-gated evidence), PartialGraphContext (overlays), getSituationTree, monteCarloWalk

Capabilities

The engine derives authorization decisions from a relation graph. What it does, in one pass:

  • Ten policy kinds compose arbitrarily: direct, tuple-to-userset (groups), chain, multi-hop, defeasible (when/unless/never/always/requires), union/intersection/exclusion (with OWA fusion), relational comparator (ABAC over values), qualitative comparator (decaying scales), challenge (proofs/MFA), parent.
  • Possibilistic semantics: decisions are maxitive rankings in [0,1], not booleans — with reliability, validity provenance, and reason codes on every result.
  • Caller-owned time: every TTL gate, decay, and proof expiry honors the caller's pinned { now }; a rerun reproduces the decision.
  • Overlays: evaluate "what if this evidence existed" without mutating the graph; caller-supplied facts ride the policy's direct relations.
  • Lossless snapshots: condensed binary serialization (~170 bytes/node) with frozen restore; malformed input fails fast and bounded.
  • Reachability: exact PLTC index with sound fast-fail and a documented null-defer contract.
  • Determinism: 251 seeded rigor campaigns — parity across normal/binary/snapshot-restored evaluation, mutation freshness, TTL contracts, complexity classes, and adversarial snapshot fuzzing.

What it does not do: no storage, no transport, no policy source of truth, no user/group management — it is a library that answers one question: may user U perform relation R on object O?

Development

npm install          # install dependencies
npm test             # full suite
npm run test:rigor   # js-rigor campaigns (property-based + fuzzing)
npm run benchmark    # compare against the committed perf baseline
npm run benchmark:save  # record a new perf baseline

CI runs the full suite, the rigor campaigns, and the benchmark on every push; v* tags additionally publish the package to the @arbiter registry.

Design Notes

  • Possibility is a maxitive ranking. Fusions preserve the weakest validity label under arbitrary dependence; conjunctive operators surface conflict mass instead of silently averaging it.
  • One evaluation path. The rule engine has a single, uncompiled evaluator — parity between normal, binary, partial-graph, and snapshot-restored checks is structural, and the rigor campaigns enforce it.
  • Snapshots are a trust boundary. Restoring untrusted bytes must produce a clean, bounded error — never a hang, a crash, or silently corrupted data. The deserializer cross-validates every count field before use.

Dependencies

Dependencies

ID Version
@tenere/pltc-core ^0.6.3
heapify ^1.0.2
uuidv7 ^1.0.2

Development Dependencies

ID Version
@arbiter/evidence-dsl ^1.0.0
@rigor/core *
@tenere/benchmark-lib ^2.0.1
fast-check ^4.5.3

Keywords

zanzibar authorization graph rebac rule-engine dsl
Details
npm
2026-08-03 19:06:41 +00:00
16
ISC
222 KiB
Assets (1)
core-1.0.3.tgz 222 KiB
Versions (9) View all
1.0.9 2026-08-05
1.0.8 2026-08-04
1.0.6 2026-08-03
1.0.5 2026-08-03
1.0.4 2026-08-03