Status: Draft Implements: ADR-OS-012 — Metering and the Billing Unit; Tenancy Spec, which names the usage ledger a control-plane table and assigns metering and entitlement to this document; Core Principles Principle 11 (Open by default, sovereign by design) Authority for: the metered unit and its counting rule; which terminal states are billable; the usage ledger (what it records, how it is written, protected and corrected); the metering service’s obligations; usage reporting and the signed export; the distinction between a commercial entitlement and an entitlement flag; where entitlement is and is not enforced. Not authority for: prices, tiers, allowances or contract terms, which are commercial and non-normative; rating and invoicing, which belong to the private synedrix-cloud class; table definitions, which stay in the Agent DB Schema; tenant isolation, which stays with the Tenancy Spec; identity, which stays with the Identity and Access Spec.


Overview

ADR-OS-012 decided what Synedrix OS counts and how it is protected: the metered unit is the governed goal, every deployment writes a tamper-evident usage ledger, self-operated deployments are billed by subscription with usage reported and never enforced, and usage pricing is reserved for the managed offering. This document turns that decision into rules a schema, a service and a test can be held to, and it defines the entitlement side that the ADR left to it.

It states what must be true. It sets no price. Where the ADR does not decide a point, this document says so and lists it in §10.

What is derived and what is proposed

Derived from the ADR and the canonProposed here, for decision
The metered unit and the billable terminal states; one ledger row per terminal goal; append-only, idempotent by source event, hash-chained, free of business content; written by a metering service and never by an agent; the same ledger in every deployment; no enforcement of usage in a self-operated deployment; the public-core and synedrix-cloud split; the entitlement flag as a business-instance value, inactive by defaultThe ledger’s exact fields and hash construction, the metering service’s failure behaviour, reconciliation, the export format, and the whole entitlement model beyond the flag: the commercial entitlement record, its states, the enforcement points and their failure behaviour

Everything in the right-hand column is a proposal in a Draft.


1. Terms

TermMeaning
Governed goalA Goal Instance that reaches a terminal state having run under a validated, activated task graph (§2)
Metered unitOne governed goal, counted once
Usage ledgerThe tenant-owned, append-only, hash-chained record of every terminal goal (§3)
Billable goalA governed goal whose terminal state is completed or partially_completed
Metering serviceThe platform component that reads terminal goal events and writes the ledger (§4)
AdjustmentA ledger row that corrects an earlier row without changing it
Commercial entitlementA grant from the offering to a tenant, or to one of its business instances, to use a capability (§6)
Entitlement flagThe business-instance value that activates a Capability Module for that instance, inactive by default (Vocabulary)
Enforcement pointA place where the platform checks an entitlement and can refuse an action (§7)

2. The metered unit

  1. The metered unit is the governed goal. It is counted once, from the terminal goal.* event of a Goal Instance (Event Taxonomy Standard §6.7). The count does not change with retries, Executions, Task counts or replans, because a Goal Instance has exactly one terminal state and one graph identity (Goal Model Spec, Task Graph Spec).

  2. A goal is governed when its task graph reached active (task.graph.activated), which means graph validation, policy evaluation and the declared approval gates applied to it. A goal that never reached an active graph, such as one blocked at capability resolution, has no governed execution to meter. It is still recorded in the ledger (§3), with governed false and billable false. Scheduled, system-initiated, test and simulation goals are not yet classified (§10). A goal whose record does not establish goal_source as tenant_request is recorded as unclassified with billable false, so no usage is charged under a rule that has not been decided.

  3. For a goal that is governed and not excluded by §2.2, the terminal state decides whether it is billable. Because §2.2 excludes every goal whose goal_source is not tenant_request, and that classification is undecided, no goal is billable under this version until it is decided (§10):

    Terminal eventStatebillable
    goal.completedcompletedtrue
    goal.partially-completedpartially_completedtrue
    goal.failedfailedfalse
    goal.blockedblockedfalse
    goal.cancelledcancelledfalse
  4. A billable goal has a unit quantity of exactly 1. There is no weight in this version. A partially_completed goal counts in full and a failed goal counts for nothing, as ADR-OS-012 §4 decides.

  5. The unit is attributed to the goal’s tenant_id and business_instance_id. It is never attributed from request input.


3. The usage ledger

3.1 What a row records

AttributeMeaning
ledger_idThe row’s identifier, server-generated
tenant_idThe owning tenant, never platform
business_instance_idThe goal’s business instance, or null if it has none
goal_idThe Goal Instance
goal_type, goal_type_versionWhat kind of goal it was
terminal_stateOne of the five terminal states
governedWhether the goal’s task graph ever reached active, taken from the goal’s task.graph.activated event (§2.2)
goal_sourcetenant_request when the goal’s own record establishes it was initiated by a request from the tenant; otherwise unclassified (§2.2)
billableDerived from governed, goal_source and §2.3: true only when governed is true, goal_source is tenant_request and §2.3 says the terminal state is billable
quantityFor a unit row, 1 for a billable goal and 0 otherwise. For an adjustment row, the signed correction (§3.2.3)
source_event_idFor a unit row, the event_id of the terminal goal.* event it came from; null for an adjustment row
chain_seqThe row’s position in the tenant’s chain: gap-free, increasing, assigned at insert (§3.3)
occurred_atThe terminal event’s timestamp
deployment_mode, data_plane_operatorThe mode and hosting variant when the goal ended, resolved as of occurred_at (§4.7)
row_kindunit for an ordinary row, adjustment for a correction
corrects_ledger_idFor an adjustment, the row it corrects; otherwise null
adjustment_key, reasonFor an adjustment, its idempotency key and the recorded reason; otherwise null
prev_hash, row_hashThe hash chain (§3.3)

The ledger holds identifiers, counts and times. It never holds intent_payload, task inputs, outputs, success-condition detail or any other business content.

3.2 Rules

  1. One row per terminal goal. UNIQUE (tenant_id, source_event_id) over unit rows makes a replayed event write nothing new. Every goal that terminates and whose tenant and as-of values can be resolved (§4.5, §4.7) has a row, including a goal that terminates as goal.blocked before acceptance (§7). A goal that cannot yet be resolved has no row, but it is held and reported, never dropped (§4.8).
  2. Append-only. A row is never updated or deleted. The writing role has INSERT and SELECT only.
  3. Corrections are adjustments. An adjustment is a new row with row_kind = adjustment, a corrects_ledger_id, a signed quantity that cancels or restates the original, a null source_event_id, a reason and an adjustment_key. UNIQUE (tenant_id, adjustment_key) makes a replayed correction write nothing new. The original row stays. A tenant’s usage total is the sum of quantity over its unit and adjustment rows.
    • An adjustment copies tenant_id, business_instance_id, goal_id, goal_type, goal_type_version, terminal_state, deployment_mode and data_plane_operator from the row it corrects. It also copies governed and goal_source, unless the correction is to one of those facts, in which case it carries the corrected value and its reason says so. Its billable is true when its quantity is non-zero, because it changes the billable total; the §2.3 derivation applies to unit rows only. Its occurred_at is the time of the correction.
    • No double correction. The writer rejects an adjustment if the sum of quantity over the corrected row and all its adjustments would fall below 0 or rise above 1, since a goal is counted at most once. A row recorded as 0 can therefore be corrected up to 1, for example when a goal recorded unclassified is later established as tenant_request, and a row of 1 can be cancelled to 0, but neither can be corrected twice in the same direction.
  4. Tenant-owned. The table follows the tenant boundary in the Agent DB Schema: non-null tenant_id, forced row-level security and tenant-led keys. A tenant credential reads its own rows only and writes none.
  5. Written by the metering service only. No agent writes the ledger, and the Audit & Lineage Agent stays read-only.
  6. Same ledger everywhere. A self-operated deployment runs the same schema and the same metering service, in a single-tenant control plane (Tenancy Spec §6.1).

3.3 The hash chain

Each tenant has one chain, ordered by chain_seq and not by occurred_at: a row written late, on catch-up, takes the next chain_seq when it is inserted. UNIQUE (tenant_id, chain_seq) holds. chain_seq is gap-free: it is assigned from a per-tenant counter row that is updated in the same transaction as the insert, so concurrent writers serialise on it and a rolled-back insert consumes no value. The counter lives in a separate per-tenant sequence table that the writing role may update, so the ledger table itself stays INSERT and SELECT only (§3.2.2). The first row’s prev_hash is a fixed genesis value for the tenant. For every later row, prev_hash is the previous row’s row_hash, and row_hash is a cryptographic hash over the row’s canonical attributes in a fixed order together with chain_seq and prev_hash. Anyone holding the rows can recompute the chain, and an altered, removed or out-of-sequence row breaks it from that point on.

A chain alone cannot show that the newest rows were deleted, because the remaining rows still link. The service therefore periodically records a signed checkpoint of the chain head (chain_seq and row_hash) in a store the ledger writer cannot modify, and verification fails if the ledger’s highest chain_seq is below the latest checkpoint’s, or if the row at that chain_seq has a different row_hash. Rows added after the latest checkpoint are covered by chain linkage only, so deletion of a tail that has not yet been checkpointed is not detectable until the next checkpoint. The chain and its checkpoints prove alteration and removal up to the last checkpoint. They do not prove that no row was withheld (§10).

The hash function, the canonical serialisation, the checkpoint interval and where checkpoints are retained are not fixed here. They are recorded as open in §10, because they affect interoperability of the export.


4. The metering service

  1. The service reads terminal goal.* events from the event layer. It trusts only the tenant_id the event layer stamped (Event Taxonomy Standard §2), never one in a payload.
  2. It writes under the tenant’s context, as a role with INSERT and SELECT on the ledger and no bypass of row-level security. It is a platform component and not an agent. It holds a tenant-bound service principal per tenant (Identity and Access Spec §5), so app.tenant_id is derived from an authenticated credential as the Tenancy Spec §4 requires, and it never sets tenant context from event content. It writes each event with the credential of the tenant the event layer stamped. In a pooled control plane that credential’s tenant_id claim is mandatory.
  3. Metering never blocks work. A goal reaches its terminal state whether or not the ledger row exists yet. The service writes the row asynchronously. If it is down, rows are written on catch-up from the durable event log, and the idempotency key makes the catch-up safe.
  4. Reconciliation. The service periodically compares terminal goal events with ledger rows for a tenant. A terminal event with no row is written. A ledger row whose source_event_id matches no event is reported as an integrity fault, never deleted.
  5. A terminal event whose tenant cannot be resolved is not written to any tenant’s ledger and is reported as a fault.
  6. Late or duplicate events are expected. The same source_event_id never produces two rows (§3.2.1).
  7. Attribution as of the event. Terminal goal.* events carry neither business_instance_id nor the deployment mode or hosting variant. The service resolves them from the goal’s own record and from a versioned configuration history valid at occurred_at, never from current configuration, so catch-up after a change gives the same answer as writing at the time. If a value cannot be resolved as of occurred_at, the row is not written with a guess or a null; it is held and reported as a fault (as in point 5). Where that history or an event field comes from is open (§10).
  8. Held goals are visible. A terminal event held under point 5 or 7 is listed in a held-goals report available to the platform operator and, when its tenant is known, to that tenant. It is written, with the next chain_seq, as soon as it can be resolved, and reconciliation (point 4) counts it as outstanding until then.

5. Usage in each deployment

DeploymentThe ledger isUsage is
Managed (Modes 1 and 2)Written by the pooled control planeThe basis of the usage charge, rated and invoiced in synedrix-cloud
Self-operated (Modes 1, 2 and 3)Written locally by the same serviceRecorded for the customer’s own reporting. Reported and never enforced

5.1 Never enforced in a self-operated deployment

A self-operated deployment never refuses, throttles or disables a goal, an agent or a capability because of its usage. A customer who runs the stack controls the meter, and enforcing by removing capability would breach Principle 11 and ADR-OS-006 §3.3. Billing in that case rests on the subscription between the parties.

5.2 Air-gapped deployments

A Mode 3 deployment writes and verifies its ledger locally and makes no outbound connection for metering. If a contract calls for usage evidence, the signed export (§5.3) is produced on the instance and carried across by the customer.

5.3 The signed export

A self-operated control plane can export a range of ledger rows together with the chain material needed to verify them and a signature, so a third party can check the rows were neither altered nor removed within the range. The export is optional. It is not the primary billing mechanism. Its format, the signature scheme and the custody and rotation of the signing key are not fixed here (§10).


6. Entitlement

6.1 Two different things

The canon already has the entitlement flag: a business-instance-owned value that activates a Capability Module for that instance, inactive by default (Taxonomy Spec). Because the instance owns the value, the flag alone cannot be a commercial control. It records which modules the instance has switched on.

This document adds a separate concept, the commercial entitlement: a grant from the offering to a tenant, or to one of its business instances, to use a capability. The two relate as follows.

Entitlement flagCommercial entitlement
Owned byThe business instanceThe offering (managed), or the contract (self-operated)
AnswersWhich modules is this instance running?Which modules is this tenant allowed to run?
EnforcedBy the instance’s own configurationOnly in the managed offering (§7)

6.2 The commercial entitlement record

A commercial entitlement is a tenant-owned record, following the tenant boundary in the Agent DB Schema. It states:

FieldMeaning
Entitlement identifier and versionA stable identifier for the grant, and a version number that increases by one with each change
Tenant and optional business instanceWho it applies to
CapabilityA Capability Module, or another commercially gated capability, by identifier
Stateactive, suspended or ended
Valid from, valid untilAn entitlement may be time-bound and ends at its end time without further action
SourceThe contract or plan reference that granted it
Granted by, granted atThe party that made it
  1. A change to a commercial entitlement is an audited event. The record is append-only: a change is a new version of the same entitlement identifier, and the history stays.
  2. State at an instant. A version takes effect at its valid-from time and supersedes earlier versions of the same identifier from that time, so a version dated in the future changes nothing until then. Valid-from must not decrease as the version number increases, and must not be earlier than the time the version is recorded, so there is no backdating and no version issued later can slot in before one already scheduled; the writer rejects a version that breaks either rule. The version in effect at an instant is the highest version whose valid-from is not later than the instant, and it is evaluated on its state and its valid-until. An earlier active version never grants anything once a later version is in effect. A capability is covered at an instant when at least one entitlement naming it, for that tenant (and instance, if scoped), has a version in effect that is active and not past its valid-until. No such entitlement means not covered, and a failure to determine this is handled by §7 rule 3.
  3. A commercial entitlement never gates a capability that Principle 11 promises (ADR-OS-006 §3.3). Only Capability Modules and other commercial value-add sit behind one.
  4. Who may create or change one in the managed offering is a platform and commercial decision. A tenant’s own roles do not change their own commercial entitlements.

7. Enforcement

Enforcement exists only in the managed offering, where the pooled control plane operates under Synedrix. Each point below is a place the managed control plane checks a commercial entitlement. This section is a proposal in a Draft.

Enforcement pointCheckOn refusal
Module activationAn instance’s entitlement flag for a Capability Module may be set active only if an active commercial entitlement covers that module for that tenant or instanceThe flag change is rejected
Goal acceptanceA tenant with no covering commercial entitlement to run governed goals does not have new goals acceptedThe goal terminates as goal.blocked, per the Event Taxonomy Standard §6.7 open item for goals that cannot be accepted, and a ledger row is written with billable false. The refusal is also recorded as an audited entitlement event naming the entitlement checked, because goal.blocked alone does not mark a commercial cause (§10)
Capability resolutionEach Capability Module a new goal needs is covered by a commercial entitlement at that moment, even if the instance’s entitlement flag is still active. Checked once, when the goal’s graph is first being activatedThe goal terminates as goal.blocked with governed and billable false, even though it was accepted, and the refusal is recorded as an audited entitlement event as above. Goals that need no uncovered module are unaffected

Rules for enforcement:

  1. Suspension never destroys. A suspended or ended entitlement blocks new activations and new goals that need the capability it covers (and, for the right to run governed goals, all new goals). It does not stop a goal whose task graph has reached active, which runs to its terminal state (a replan that adds a module is not re-checked), and it never deletes a tenant’s data.
  2. Export is always available. A tenant can always read its own ledger rows and export its own data, whatever the entitlement state.
  3. Fail closed, with a limit. If the managed control plane cannot determine a tenant’s entitlement state at an enforcement point, it refuses the new activation or goal. It does not guess in the tenant’s favour. How long a cached state may be used during an outage is not decided (§10).
  4. No enforcement of usage volume. ADR-OS-012 sets no allowance or quota, so this version has no enforcement point on goal count. It is added only by a later decision.
  5. Self-operated deployments have no enforcement points. The entitlement flag there is configuration, and no commercial entitlement is checked. The contract carries the commercial relationship.

8. Open-core placement

Public corePrivate synedrix-cloud class
The ledger schema and its tenant boundary, the metering service, the billable rule, hash chaining and verification, the export format, the commercial entitlement record formatRating, invoicing and billing, the managed offering’s enforcement points, prices and plans, and the platform’s authority to create commercial entitlements

The ledger and the format of an entitlement are inspectable by the customer, so they sit in the public core. Turning them into money and refusing actions sit in the private class, as ADR-OS-006 §3.5 places billing.


9. Conformance

CheckExpected result
A goal reaches a terminal state and its terminal event is replayedOne ledger row exists; the replay writes nothing
A goal is retried, replanned and its tasks executed several times, then completesExactly one billable row with quantity 1
A goal terminates failed, blocked or cancelledA row exists with billable false and quantity 0
A goal terminates partially_completedA billable row with quantity 1
A goal never reached an active graphA row exists with governed and billable false
A goal is refused at goal acceptance or capability resolutionThe goal terminates goal.blocked; a row exists with billable false
A correction row is replayedNothing new is written
Ledger rows up to or before the latest checkpoint are deletedVerification against the checkpoint fails
Only rows after the latest checkpoint are deletedNot detected until the next checkpoint (stated limit)
A goal’s tenant or as-of values cannot be resolvedNo row yet; the goal is in the held-goals report and is written when resolved
Two adjustments would together correct a row below zeroThe second is rejected
A goal has no established tenant_request sourceRecorded unclassified with billable false
A future-dated entitlement version existsIt changes nothing until its valid-from; the earlier version applies until then
A row is written late, on catch-upIt takes the next chain_seq, and the chain verifies
An entitlement is suspended after an earlier active versionOnly the version in effect at that instant is evaluated; the module is not covered once the suspension takes effect
An entitlement version has a valid-from earlier than its predecessor’s, or earlier than its recording timeRejected
A module’s entitlement ends while its instance flag stays activeA new goal needing that module is blocked at capability resolution
The ledger role attempts UPDATE or DELETE on a ledger rowDenied
A ledger row is altered, removed or out of sequenceChain verification fails from that row onward
A correction is neededA new adjustment row references the original; the original is unchanged
A tenant credential reads or writes another tenant’s ledger rowsNo rows returned; write denied
The ledger is scanned for business contentNone found
The metering service is stopped while goals terminate, then restartedEvery terminal event has a row after catch-up, none duplicated
A terminal event whose tenant cannot be resolvedWritten to no tenant’s ledger; reported as a fault
A self-operated deployment exceeds any usage figureNothing is refused, throttled or disabled
An air-gapped instance meters usageNo outbound connection is made
The managed control plane receives a module activation with no active commercial entitlementThe flag change is rejected
An entitlement is suspended while a goal is in flightThe goal runs to its terminal state; no new goal is accepted
The entitlement state cannot be determined at an enforcement pointThe new activation or goal is refused

10. Open items

  • Hash construction. The hash function, canonical serialisation, checkpoint interval and checkpoint store for the chain are not fixed.
  • Event-time attribution source. Whether business_instance_id, deployment mode and hosting variant come from a new terminal-event field or a versioned configuration history (§4.7) is not decided.
  • Signing key custody (§5.3). Who holds the export signing key in a self-operated deployment, and how it is rotated without breaking verification, is not decided.
  • Evidence of absence. The chain shows alteration, not a metering service that was stopped or rows that were never written. Whether that matters under a subscription is a commercial question.
  • Complexity and weights. One unit per goal treats a trivial goal and a very large one alike. Tiers or weights by Goal Type are not designed.
  • What counts as governed. Scheduled, system-initiated, test and simulation goals are not classified. §2.2 records them as unclassified, with billable false, until they are. The Goal Model Spec now records a goal’s origin as requested_by_type (user, agent, workflow, external_system or schedule), but which of those count as tenant_request is not decided, so until it is the metering service has no basis to set tenant_request; deciding that mapping is a prerequisite for billing under this spec.
  • Goals rejected before acceptance. The Goal Model Spec has no rejection transition. Until it gains one, such a goal terminates as goal.blocked (Event Taxonomy Standard §6.7), is recorded with billable false, and this spec adopts that. How the event distinguishes an entitlement refusal from a malformed intent or an authority refusal is not defined. A dedicated goal.rejected event would replace it.
  • Entitlement outage behaviour (§7.3). How long a cached entitlement state may be used during a control-plane outage is not decided.
  • Allowances and quotas. None exists. If one is adopted, it needs an enforcement point and a decision on what happens at the limit.
  • Periods, disputes and retention. Billing period boundaries, time zones, the dispute process and how long ledger rows and entitlement history must be kept, including after a tenant ends, are not defined.
  • Who creates a commercial entitlement. The platform’s authority and the audit it carries are not specified.
  • Table definitions. The ledger and the commercial entitlement tables are not yet in the Agent DB Schema.
  • Event names. Entitlement and metering-fault events need names in the Event Taxonomy Standard. None is proposed here.

Versioning


Core Principles and Canonical Reference

Decisions

  • ADR-OS-012 — Metering and the Billing Unit — The decision this document implements.
  • ADR-OS-008 — Tenancy Model — Places the usage ledger in the control plane.
  • ADR-OS-006 — Repository Topology and Open-Core Boundary — §3.3 and §3.5 set the open-core placement in §8.

Specifications this document depends on


Synedrix OS · October 2026