A permission is not enough

In most systems a user either has a right or does not. Real organisations are more careful than that — and so is the platform.

Four axes

1. Action

Does the person hold the right — for example, to approve a payout?

2. Record

Is this row theirs? That can mean their own records, their organisation, or their branch of the hierarchy.

3. Field

May they see this field — and may they change it?

4. Session trust

Holding the right is not enough: the session must reach the trust level the action requires — a plain session, a password or key, a second factor, or a signature. Each organisation sets its own scale.

Access is checked on three axes: right, session trust and the rowApprove a payoutHas the rightpayout.approve?Session trustat least strong?Payout in theuser's regions?Yes: statusmoves to ApprovedNo: refusedNo: confirm witha second factorNo: row notvisibleyesyesyes
A request passes the right, then the session trust, then the row. Each «no» has its own answer.

Three answers, not two

  • Allow
  • Deny
  • You have the right — confirm with a stronger factor first
A finance manager holds the right to approve payouts but signed in with a password only. The system does not say «no»; it says «yes, after a second factor». Her deputy can do almost everything she can, except the rights marked as non-delegable.

How it is declared

In Cambium, the language of the platform's business logic, a package declares its rights once, each with the lowest session trust it needs; a row policy decides which records a user sees; a status move names the right it needs and the condition it must meet:

/// Rights in groups; each names the lowest session trust it needs.
permissions {
    payout level strong {
        read level identified,
        edit,
        approve implies read,
    },
    all_regions,
    director level proven,
}

/// Approval is a manual edge: a right and a condition; above the limit the director must join.
machine Payout.status {
    Draft -> Approved permission if amount < config.payout.approve_limit { payout.approve } else { payout.approve and director }
                      ensure amount > 0 usd and note != "" else (err.payout.state);
}

/// Row policy: a user sees only the payouts of their own regions.
policy payout.read(p: Payout, actor: Actor) -> bool {
    actor.regions.contains(p.region) or actor.has(all_regions)
}
  • Reading payouts needs an identified session, approving needs a second factor, the director right needs identity proven with a document
  • The row policy is added to every query and command automatically — a user outside the region simply does not see the payout
  • Above the configured limit, approval needs the director as well
  • The right and the condition are checked before the status changes; missing trust returns the «confirm with a stronger factor» form instead of a refusal
  • A move that is not declared cannot happen, whatever rights a user holds

Trust levels form one scale: anonymous, identified, confirmed, strong (second factor), proven (identity document) and qualified (qualified-certificate signature). A package sets the lower bound; an organisation may raise it, never lower it.

Delegation, suspension and history

  • Some rights can never be delegated
  • Suspending a person removes everything at once, including the rights they delegated
  • Grants have validity periods
  • Every denied attempt is recorded in the audit log
  • The log of grants is the source of truth, so the access state can be rebuilt and audited for any past moment

For the most critical operations

The most critical decisions require weighted k-of-n signatures: several people, each with a weight, must agree. Irreversible actions can require a delay or a second person. Signatures are hybrid — Ed25519 together with post-quantum ML-DSA-65.

Next step

See it on your own business

Book a demo: we show the platform on your processes and agree where your data will live — in our cloud or on your own nodes.