SPEC_V0.1.0 — DRAFT

$403 Protocol Specification

Programmable Access Control on Bitcoin SV

The normative specification for the $403 Conditions Machine — on-chain finite state machines for permissions, restrictions, and programmable access control, enforced by sCrypt smart contracts and validated through Proof of Indexing.

@b0ase·February 2026·Draft — Subject to Change

Status: This specification is in DRAFT status. Implementations SHOULD NOT treat this as stable. The key words “MUST”, “MUST NOT”, “SHOULD”, “MAY” are to be interpreted as described in RFC 2119.

Contents

1. Introduction

HTTP 403 means Forbidden — the server understood the request but refuses to authorize it. The $403 protocol extends this concept to the programmable web: conditions that determine access are no longer ad-hoc application logic but first-class on-chain objects — inspectable, composable, transferable, and enforced by the Bitcoin network itself.

$403 is the third protocol in the $40x stack:

$401 — IDENTITY

Who you are. Root key, OAuth strands, attestations.

$402 — PAYMENT

What you pay. Tokenized URL access, micropayments.

$403 — CONDITIONS

Whether you may. Programmable rules, gates, restrictions.

This document specifies the data structures, state machine semantics, on-chain encoding, guard expression language, Proof of Indexing mechanism, and integration points of the $403 protocol.

2. Terminology

ConditionAn on-chain finite state machine (FSM) that evaluates to a permission state (e.g. LOCKED, UNLOCKED, EXPIRED). The atomic unit of the $403 protocol.
GuardA boolean expression attached to a state transition. The transition fires if and only if the guard evaluates to true.
EffectAn action triggered when a condition enters a specific state (e.g. grant access to a $402 resource, revoke a token).
SubjectThe $401 identity (root key) that the condition applies to.
IssuerThe entity that created the condition. May or may not be the same as the subject.
IndexerA node that evaluates condition state transitions and produces Proof of Indexing submissions.
AttestorA trusted third party that provides signed data inputs for guard evaluation (e.g. KYC provider, geolocation oracle).
IrrevocableA condition flag indicating that no key — including the issuer — can bypass or remove the condition before its programmed resolution.
PoIProof of Indexing. A Merkle-root commitment of state transitions, combined with Proof-of-Work, that proves an indexer correctly evaluated a batch of conditions.
StrandA $401 identity attestation (e.g. a verified GitHub account, a government ID check, a service credential).

3. Protocol Overview

The $403 protocol defines a system where access conditions are:

01
Inscribed. Conditions are written to the BSV blockchain as UTXOs containing sCrypt smart contracts.
02
Evaluated. Guard expressions are checked against current state: $401 identity data, block height, timestamps, oracle attestations.
03
Transitioned. When a guard is satisfied, the condition state machine advances. The UTXO is spent and a new one created with the updated state.
04
Indexed. Indexers process batches of transitions, construct Merkle trees, and submit Proof of Indexing on-chain.
05
Enforced. Other protocols ($402 payment, application logic) query condition state before granting access. Invalid transitions are rejected by miners.
Request Flow
User presents $401 identity
$403 evaluates conditions against identity + context
[LOCKED] ——— 403 Forbidden ——— [UNLOCKED]
$402 processes payment / grants access

4. Condition Object

A Condition is the atomic unit of the $403 protocol. Every condition MUST contain the following fields:

Condition Schema
// $403 Condition Object (JSON representation)
{
"id": "$403/{class}/{name}"// Unique condition identifier
"version": "0.1.0"// Protocol version
"issuer": "$401:{pubkey}"// Issuer identity (root key)
"subject": "$401:{pubkey}"// Subject identity (who it applies to)
"class": "securities | age | self | geo | identity | temporal | composite"
"irrevocable": true | false// Can issuer bypass before resolution?
"created": {block_height}// Block height at inscription
"states": ["LOCKED", "UNLOCKED", "EXPIRED"]// Finite set of states
"initial": "LOCKED"// Initial state
"transitions": [...]// Array of Transition objects
"effects": { "UNLOCKED": "grant($402/resource)" }// State → effect mapping
}
Required Fields
idUnique identifier, namespaced by class
issuer$401 root key of creator
subject$401 root key of target
statesArray of 2–8 state names
initialStarting state (MUST be in states[])
transitionsAt least 1 valid transition
Constraints

States array MUST contain 2–8 entries.

State names MUST be uppercase ASCII, max 32 chars.

Every state MUST be reachable from initial.

If irrevocable: true, the issuer key MUST NOT appear in any guard as a bypass authority.

Conditions MAY reference other conditions by ID (composition).

5. State Machine

Every $403 condition is a deterministic finite state machine (FSM). The FSM is defined by:

S= finite set of states { s₁, s₂, ..., sₙ } where n ∈ [2, 8]
s₀= initial state, s₀ ∈ S
T= set of transitions { (sᵢ, sⱼ, guard) } where sᵢ, sⱼ ∈ S
G= guard function: context → boolean
E= effect mapping: S → action (optional)

A transition from state sᵢ to sⱼ fires if and only if:

  1. The current state is sᵢ
  2. A valid transition (sᵢ → sⱼ) exists in T
  3. The guard G(context) evaluates to true
  4. The transition transaction is validly signed

The FSM is deterministic: given the same state and context, the same transitions fire. The guard language is intentionally NOT Turing-complete — all guard evaluations MUST terminate in bounded time.

6. Guard Expression Language

Guards are boolean expressions written in a restricted predicate language. The language supports:

Operators
Comparison== != > < >= <=
LogicalAND OR NOT
MembershipIN NOT_IN
ExistenceEXISTS NOT_EXISTS
Data Sources
$401Identity strands, attestations
$402Payment state, token balances
blockheight, timestamp, hash
oracleSigned attestation data
$403Other condition states
Guard Expression Examples
// Age gate: subject must be 18+ (verified by gov-id strand)
$401.strand("gov-id").age >= 18
// Geographic restriction: not in sanctioned jurisdictions
$401.strand("geo").country NOT_IN ["KP", "IR", "SY", "CU"]
// Time lock: block height must exceed anchor + 52560
block.height > condition.created + 52560
// Composite: age AND geo AND payment
$403("age-gate-18").state == "UNLOCKED"
AND $403("geo-uk").state == "UNLOCKED"
AND $402.balance(subject) >= 1000
// Identity threshold: at least 3 verified strands
$401.strand_count(subject) >= 3
// Self-imposed savings lock (irrevocable)
block.height >= 890000

Restriction: No loops, no recursion, no unbounded computation. Guard evaluation MUST complete in O(n) time where n is the expression depth. Maximum expression depth: 16. Maximum referenced conditions in a single guard: 8.

7. State Transitions

A Transition object defines a single edge in the state machine graph:

Transition Schema
// Transition Object
{
"from": "LOCKED"// Source state
"to": "UNLOCKED"// Target state
"guard": "$401.strand('gov-id').age >= 18"// Boolean expression
"authority": "subject | issuer | any"// Who may trigger
"cooldown": 0// Min blocks between triggers
}

Authority

subject — only the subject's $401 key can trigger.issuer — only the issuer can trigger.any — anyone with a valid guard proof can trigger (used for time-based auto-transitions).

Cooldown

Minimum number of blocks that MUST pass between consecutive triggers of the same transition. Prevents rapid state oscillation. MUST be 0 or greater. A value of 0 means no cooldown.

Irrevocable Transitions

When irrevocable: true is set on the condition, reverse transitions (back to a previous state) MUST NOT list the issuer as authority. The issuer cannot undo the condition. Only the guard logic can advance state. This is critical for self-imposed restrictions (savings locks, vesting schedules).

8. On-Chain Representation

Each $403 condition exists as a UTXO on BSV containing an sCrypt smart contract. State transitions spend the current UTXO and create a new one with the updated state, forming an immutable chain of state changes.

sCrypt Contract Structure
// Simplified $403 sCrypt Contract
class Condition403 extends SmartContract {
@prop() states: FixedArray<ByteString, 8>
@prop(true) currentState: bigint
@prop() transitions: FixedArray<Transition, 16>
@prop() issuer: PubKey
@prop() subject: PubKey // $401 root key
@prop() irrevocable: boolean
@prop() createdAt: bigint // Block height
// Transition: evaluate guard, update state
@method()
public transition(
transitionIdx: bigint,
guardProof: ByteString, // Merkle proof of guard inputs
sig: Sig // Authority signature
) {
// 1. Verify transition exists for currentState
// 2. Verify guard evaluates to true
// 3. Verify signature matches authority
// 4. Update currentState
// 5. Propagate UTXO with new state
}
}
Inscription

New condition → Deploy sCrypt contract as UTXO. Initial state set. Condition ID derived from txid.

Transition

Spend current UTXO → new UTXO with updated currentState. Auditable on-chain history.

Query

Read latest UTXO for condition ID. Current state is currentState. No transaction needed for reads.

9. Proof of Indexing

Conditions are only useful if they are correctly evaluated. Proof of Indexing (PoI) ensures that indexers who report condition states have actually done the computation.

PoI Protocol

01

Indexer processes a batch of N condition evaluations (recommended: N = 1000).

02

For each evaluation, record tuple: (condition_id, prev_state, new_state, guard_inputs_hash, block_height).

03

Construct a Merkle tree over all tuples. The root is the batch commitment.

04

Compute a PoW nonce meeting the current difficulty target: SHA256(merkle_root || nonce) < target.

05

Inscribe on-chain: { merkle_root, nonce, batch_size, indexer_pubkey, stake_txid }.

06

Challenge window: 144 blocks (~24h). Any party may request a Merkle proof for any condition_id in the batch.

07

If challenged, indexer MUST produce the Merkle proof. Failure to respond or incorrect proof: stake is slashed.

08

After challenge window: PoI is finalized. Indexer earns $403 tokens proportional to batch_size.

Stake Requirements
Minimum Stake1,000 $403
Slash Penalty100% of stake
Challenge Window144 blocks
RewardDynamic (per batch)
Security Properties

Unforgeable cost: PoW nonce requires real energy expenditure.

Verifiable: Merkle proofs are O(log n) to verify.

Challengeable: Any incorrect state can be proven wrong.

10. Permission Classes

The $403 protocol defines seven standard permission classes. Each class has distinct guard patterns, authority models, and legal considerations:

Securities: Accredited investor checks, lock-up periods, Reg D/S geographic restrictions, KYC/AML gates. Default: LOCKED. Issuer-imposed. Non-bypassable.
Age: Content ratings (13+, 16+, 18+, 21+). Requires trusted attestor. One-way ratchet — age verification doesn't revert. Parental override via co-signature.
Self-Imposed: Savings locks, pension plans, spending limits, cooling-off periods. IRREVOCABLE once set. Commitment technology — the blockchain as pre-commitment device.
Geographic: Sanctions compliance, data residency, gambling licenses. Dynamic — re-evaluates on jurisdiction change. Supports REVOCATION back to LOCKED.
Identity: Strand count minimums, specific provider requirements, reputation thresholds, group membership. Composes directly with $401. Lightest class.
Temporal: Time-locked releases, auction windows, vesting schedules, subscription expiry. Simplest class — guards reference only block height. Purely deterministic.
Composite: AND/OR composition, multi-sig conditions, cascading fallbacks, conditional payments. Conditions-of-conditions. Bounded depth (max 16).

11. Integration: $401 & $402

The three protocols form a complete access control stack. Integration points are well-defined:

Access Request Flow
// 1. User presents identity
request.identity = $401.resolve(user_pubkey)
// 2. $403 evaluates all conditions bound to the resource
for condition in resource.conditions:
state = $403.evaluate(condition.id, request.identity, context)
if state != "UNLOCKED":
return 403 Forbidden { condition: condition.id, state: state }
// 3. All conditions passed — process payment
$402.process_payment(resource, request.identity)
// 4. Grant access
return 200 OK { token: access_token }
$401 Interface

$401.resolve(pubkey) → Identity object with strands

$401.strand(type) → Specific attestation data

$401.strand_count() → Number of verified strands

$401.verify(attestation) → boolean

$402 Interface

$402.balance(pubkey) → Token balance

$402.payment_state(resource) → Paid / Unpaid

$402.bind_condition(resource, condition_id) → Link

$402.process_payment(resource, identity) → Receipt

12. Wire Format

$403 messages are encoded as JSON over HTTPS or as on-chain OP_RETURN data. Two primary message types:

EVALUATE Request
POST /403/evaluate
Content-Type: application/json
{
"condition_id": "$403/age-gate/18+"
"subject": "$401:02a1b2c3..."
"context": { "block_height": 889500, "timestamp": 1739750400 }
"attestations": [{ "provider": "gov-id", "data": "...", "sig": "..." }]
}
EVALUATE Response
{
"condition_id": "$403/age-gate/18+"
"state": "UNLOCKED"
"previous": "LOCKED"
"transition_txid": "abc123..."
"block_height": 889501
"poi_root": "def456..."// Proof of Indexing reference
}

13. Token Specification

The $403 token governs the conditions network. It is earned through mining and indexing, and staked by condition evaluation nodes.

Token Parameters
Symbol$403
StandardBSV-21
MintingPoW20 Hash-to-Mint
ContractsCrypt (Bitcoin Script)
Total Supply21,000,000
Decimals8
Block RewardDynamic (halving)
DifficultyRetargets per epoch
Token Utility

Mining: Hash-to-mint. Submit valid PoW to contract, receive $403.

Indexing: Earn $403 by submitting valid Proof of Indexing batches.

Staking: Stake $403 to run a condition evaluation node. Required for PoI submission.

Governance: $403 holders vote on protocol parameter changes (batch size, difficulty, stake minimum).

14. Security Considerations

The $403 security model rests on three pillars:

Script Enforcement

Conditions are enforced by Bitcoin Script via sCrypt contracts. Miners validate every state transition. An invalid transition is an invalid transaction — it MUST NOT be mined. This provides the same security guarantees as Bitcoin's double-spend prevention.

Proof of Indexing

Indexers MUST prove their work. The Merkle tree of state transitions is publicly challengeable. False claims result in full stake slashing. The economic incentive for honest indexing mirrors Bitcoin mining game theory.

Irrevocability

Self-imposed restrictions are cryptographically irrevocable. No key, no social recovery, no admin override. The script will not execute until the programmed condition is met. This is what makes $403 useful as a commitment device.

Guard Bounded Evaluation

The guard expression language is intentionally restricted: no loops, no recursion, maximum depth of 16. All evaluations terminate in bounded time. This prevents denial-of-service via complex guard expressions.

15. Open Questions

This specification intentionally leaves several areas for future work:

Oracle Trust Model: Geographic and age conditions require external attestation data. The specification does not yet define the oracle trust model — how attestors are selected, how their data is verified on-chain, or how oracle manipulation is prevented.
Fee Model: Who pays for condition evaluation? The subject, the issuer, or the network via token inflation? Each model has different incentive properties.
Guard Language Formalization: The guard expression language needs a formal grammar (BNF or PEG). The current description is normative but not machine-parseable.
Privacy / Zero-Knowledge: Condition evaluation reveals information about the subject. ZK proofs could allow evaluation without revealing underlying data. Architecturally possible but adds complexity.
Upgrade Path: What happens when regulations change? Versioned conditions with sunset clauses are proposed but not specified.
Cross-Chain Conditions: Can conditions reference state on other chains? The current spec is BSV-only. Cross-chain bridge conditions are out of scope for v0.1.

$403 Protocol Specification — Draft v0.1.0 — February 2026 — @b0ase

Not legal advice. Not financial advice. Subject to change.

$402Download