From 0c9408f48c25f3dc6eaf8f99c7f2d0dbf572519a Mon Sep 17 00:00:00 2001 From: Andre Ambrosio Date: Mon, 20 Apr 2026 10:08:08 -0300 Subject: [PATCH 1/5] ci: add Dependabot workflow --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1705d1b --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 From e15aa37a67d230c9230f67eccc30964d20197a65 Mon Sep 17 00:00:00 2001 From: Andre Ambrosio Date: Mon, 20 Apr 2026 10:45:24 -0300 Subject: [PATCH 2/5] docs: architecture + glossary + error catalog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docs/ARCHITECTURE.md: system diagram + sequence diagrams for create BEO, grant ConsentToken, submit BioRecord, and destroy BEO flows. Component responsibilities + trust boundaries documented. - docs/GLOSSARY.md: canonical definitions for BEO, IEO, BioRecord, ConsentToken, Scope, Intent, Domain, Guardian, Recovery, Relayer, and taxonomy terms. Normative — all implementations MUST use these meanings. - docs/ERROR_CODES.md: consolidated stable error code catalog cross-linked to contracts, API, SDK, CLI, and MCP sources. --- docs/ARCHITECTURE.md | 230 +++++++++++++++++++++++++++++++++++++++++++ docs/ERROR_CODES.md | 130 ++++++++++++++++++++++++ docs/GLOSSARY.md | 138 ++++++++++++++++++++++++++ 3 files changed, 498 insertions(+) create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/ERROR_CODES.md create mode 100644 docs/GLOSSARY.md diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..0962882 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,230 @@ +# BSP — Architecture + +> How the Biological Sovereignty Protocol fits together, end to end. +> Version 0.2 — April 2026 + +--- + +## Overview + +BSP is layered. The user holds the keys. The SDK and CLI sign operations locally. A gasless relayer submits transactions to Aptos Move contracts. Payloads are persisted on Arweave for permanence. No component in the chain can forge or modify a user's actions without the user's key. + +--- + +## System Diagram + +```mermaid +flowchart LR + User([User / Patient]) -->|opens| Frontend[bsp-id-web
Frontend] + User -->|runs| CLI[bspctl
CLI] + User -->|delegates to| AI[AI Assistant] + + AI -->|via MCP| MCP[bsp-mcp
MCP Server] + + Frontend -->|uses| SDK[bsp-sdk
TS / Python] + CLI -->|uses| SDK + MCP -->|uses| SDK + + SDK -->|signed payload
Ed25519| API[Registry API
Gasless Relayer] + API -->|submits TX
pays gas| Contracts[Aptos Move
Contracts] + API -->|persists| Arweave[(Arweave
Permanent Storage)] + + Contracts -->|verifies signature
on-chain| Contracts + Contracts -->|emits events| Indexer[Indexer] + Indexer -->|reads| API + + style User fill:#fff2cc,stroke:#d6b656 + style Contracts fill:#d5e8d4,stroke:#82b366 + style Arweave fill:#dae8fc,stroke:#6c8ebf +``` + +### Layers + +| Layer | Component | Role | +|---|---|---| +| **Presentation** | `bsp-id-web`, `bspctl`, AI assistants | User-facing surfaces | +| **Client SDK** | `bsp-sdk-typescript`, `bsp-sdk-python` | Ed25519 signing, type definitions, payload builders | +| **Transport** | Registry API (relayer) | Gasless submission of signed payloads to chain | +| **Protocol** | Aptos Move contracts | Source of truth — BEO, IEO, ConsentToken, AccessControl | +| **Storage** | Arweave | Permanent BioRecord persistence | + +--- + +## Trust Boundaries + +| Boundary | Who trusts what | +|---|---| +| User ↔ Client (SDK/CLI/Web) | User trusts their machine and the signing code they run | +| Client ↔ Relayer | Client trusts nothing — payloads are signed. Relayer cannot forge. | +| Relayer ↔ Chain | Chain verifies every signature. Relayer is only a gas payer. | +| Chain ↔ Arweave | Chain pins the Arweave TX ID. Arweave guarantees permanence. | + +The relayer is **infrastructure, not authority**. A compromised relayer cannot modify consent, forge records, or destroy a BEO. The worst a rogue relayer can do is refuse to relay — in which case any other relayer (or the user directly) can take over. + +--- + +## Sequence: Create a BEO + +```mermaid +sequenceDiagram + actor User + participant App as Client
(SDK / CLI / Web) + participant API as Registry API
(Relayer) + participant Chain as Aptos Move
Contract + participant AR as Arweave + + User->>App: Choose domain (e.g. alice.bsp) + App->>App: Generate Ed25519 keypair locally + App->>App: Build BEO payload + sign + App->>API: POST /beo (signed payload) + API->>Chain: submit_create_beo(payload, sig) + Chain->>Chain: Verify Ed25519 signature + Chain->>Chain: Check domain availability + Chain->>AR: Pin BEO metadata + AR-->>Chain: tx_id + Chain-->>API: BEO created (beoId, txId) + API-->>App: Response + App-->>User: Show keys (ONCE) + BEO ID + + Note over User: User stores private key
OFFLINE. Never transmitted. +``` + +--- + +## Sequence: Grant ConsentToken + +```mermaid +sequenceDiagram + actor User + participant App as Client (holder) + participant API as Registry API + participant Chain as AccessControl
Contract + participant IEO as Institution + + User->>App: Grant consent to IEO
(intents + categories + expiry) + App->>App: Build token payload + App->>App: Sign with BEO private key + App->>API: POST /consent/grant + API->>Chain: issue_token(payload, sig) + Chain->>Chain: Verify holder signature + Chain->>Chain: Validate IEO exists + active + Chain->>Chain: Mint ConsentToken (tokenId) + Chain-->>API: ConsentToken created + API-->>App: tokenId + details + App-->>User: Show tokenId + User->>IEO: Share tokenId
(out of band) + + Note over Chain: Token state is on-chain.
Revocation is immediate. +``` + +--- + +## Sequence: Submit BioRecord (IEO → BEO) + +```mermaid +sequenceDiagram + participant IEO as Institution
(Lab / Hospital) + participant API as Registry API + participant Chain as Contracts + participant AR as Arweave + + IEO->>IEO: Build BioRecord
(category, code, value, refs) + IEO->>IEO: Sign with IEO private key + IEO->>API: POST /records/submit
(beoId, tokenId, record, sig) + API->>Chain: verify_consent(tokenId, intent=SUBMIT_RECORD) + Chain-->>API: OK (scope matches) + API->>Chain: record_submission(beoId, recordId, sig) + Chain->>AR: Persist full BioRecord JSON + AR-->>Chain: arweave_tx_id + Chain->>Chain: Link recordId → arweave_tx_id + Chain-->>API: Submission confirmed + API-->>IEO: recordId + arweaveTxId +``` + +--- + +## Sequence: Destroy BEO (Cryptographic Erasure) + +```mermaid +sequenceDiagram + actor User + participant App as Client + participant Chain as Contracts + participant AC as AccessControl + participant Dom as Domain Registry + + User->>App: bsp destroy --confirm + App->>App: Build destruction payload + sign + App->>Chain: submit_destroy_beo(payload, sig) + Chain->>Chain: Verify holder signature + Chain->>Chain: Nullify public key
(cryptographic erasure) + Chain->>AC: Revoke ALL ConsentTokens + AC-->>Chain: All tokens revoked + Chain->>Dom: Release .bsp domain + Dom-->>Chain: Domain released + Chain->>Chain: Wipe recovery config + Chain->>Chain: Set status = DESTROYED + Chain-->>App: Destruction confirmed + App-->>User: BEO destroyed — no undo + + Note over Chain: Arweave records remain
but are now unreadable
(key nullified). + Note over User: Implements LGPD Art. 18
& GDPR Art. 17. +``` + +--- + +## Component Responsibilities + +### `bsp-sdk` (TypeScript / Python) +- Ed25519 keypair generation and signing +- Canonical payload serialization +- Type definitions for BEO, IEO, ConsentToken, BioRecord +- `ExchangeClient` — fetches records with on-chain consent verification + +### `bspctl` (CLI) +- 22 commands covering full protocol lifecycle +- Local config at `~/.bsp/config.json` +- Delegates signing to `bsp-sdk` +- No server-side state + +### `bsp-mcp` (MCP Server) +- stdio transport for AI assistants +- `ConsentGuard` gates every data-access tool +- Enforces intent + expiry + on-chain revocation state + +### `bsp-id-web` (Frontend) +- React + Vite web app +- Generates keys in-browser (never sent to server) +- Issues ConsentTokens through a visual flow + +### `bsp-registry-api` (Relayer) +- Receives signed payloads +- Submits Aptos transactions +- Pays gas on behalf of users +- Cannot modify or forge — signatures are verified on-chain + +### Aptos Move Contracts +- `BEO` — sovereign biological identity +- `IEO` — institutional entity +- `AccessControl` — ConsentToken issuance, revocation, scope checks +- `DomainRegistry` — `.bsp` namespace + +### Arweave +- Permanent storage for BioRecord JSON +- Chain stores only the Arweave TX ID pointer +- Economic incentive ensures 200+ year persistence + +--- + +## Extensibility + +New biomarkers, intents, and IEO types are added through the BIP (BSP Improvement Proposal) process. See `../bip/BIP-0000-template.md` and `spec/governance.md`. + +--- + +## Related + +- **Glossary** — `docs/GLOSSARY.md` +- **Error Codes** — `docs/ERROR_CODES.md` +- **Implementation Guide** — `docs/implementation-guide.md` +- **Specification** — `spec/overview.md` diff --git a/docs/ERROR_CODES.md b/docs/ERROR_CODES.md new file mode 100644 index 0000000..e74a006 --- /dev/null +++ b/docs/ERROR_CODES.md @@ -0,0 +1,130 @@ +# BSP — Error Codes + +> Canonical error catalog. Every BSP implementation (contracts, API, SDK, CLI, MCP server) MUST emit these codes on failure. +> Codes are stable — they will not be renamed across minor versions. + +--- + +## How error codes work + +Every BSP error carries: + +| Field | Type | Description | +|---|---|---| +| `code` | string | Stable machine-readable identifier (this catalog) | +| `message` | string | Human-readable description | +| `source` | string | Which layer raised it: `contract` \| `api` \| `sdk` \| `cli` \| `mcp` | +| `details` | object | Optional structured context (IDs, timestamps, etc.) | + +Clients MUST NOT parse error messages. Always branch on `code`. + +--- + +## Identity (BEO / IEO) + +| Code | Source | Meaning | +|---|---|---| +| `BEO_NOT_FOUND` | contract, api | BEO ID does not exist on chain | +| `BEO_LOCKED` | contract | Operation rejected — BEO is in `LOCKED` status | +| `BEO_DESTROYED` | contract | Operation rejected — BEO has been permanently destroyed | +| `BEO_ALREADY_EXISTS` | contract | Domain already bound to an existing BEO | +| `DOMAIN_INVALID` | contract, sdk | `.bsp` domain format invalid (lowercase, `.bsp` suffix, allowed chars) | +| `DOMAIN_RESERVED` | contract | Domain is reserved (e.g. `admin.bsp`, `root.bsp`) | +| `IEO_NOT_FOUND` | contract, api | IEO ID does not exist | +| `IEO_INACTIVE` | contract | IEO is locked or destroyed — cannot receive or submit | +| `IEO_TYPE_INVALID` | sdk | IEO type not in enum (`LAB`, `HOSPITAL`, `WEARABLE`, ...) | +| `KEY_VERSION_MISMATCH` | contract | Signature was produced with an outdated key version | + +--- + +## Consent + +| Code | Source | Meaning | +|---|---|---| +| `CONSENT_REQUIRED` | mcp, api | No ConsentToken configured or passed for this operation | +| `TOKEN_NOT_FOUND` | contract, api | ConsentToken ID does not exist | +| `TOKEN_EXPIRED` | contract, mcp | Token `expiresAt` has passed | +| `TOKEN_REVOKED` | contract, mcp | Token was revoked by the holder | +| `INTENT_NOT_AUTHORIZED` | contract, mcp | Required intent not present in token.intents | +| `SCOPE_VIOLATION` | contract | Requested category is outside token.categories | +| `TOKEN_SIGNATURE_INVALID` | contract | Signature on token payload does not match BEO public key | + +--- + +## Cryptography / Signatures + +| Code | Source | Meaning | +|---|---|---| +| `SIGNATURE_INVALID` | contract, sdk | Ed25519 verification failed | +| `SIGNATURE_MISSING` | api | Request payload not signed | +| `NONCE_REPLAY` | contract, api | Nonce already used — replay attack detected | +| `NONCE_TOO_SHORT` | sdk | Nonce length below 16-char minimum | +| `TIMESTAMP_TOO_OLD` | contract, api | Request timestamp older than 5 minutes | +| `TIMESTAMP_IN_FUTURE` | contract | Request timestamp beyond acceptable skew | +| `PUBLIC_KEY_NULLIFIED` | contract | BEO key has been nullified (destroyed BEO) | + +--- + +## BioRecord / Data + +| Code | Source | Meaning | +|---|---|---| +| `RECORD_NOT_FOUND` | api, contract | BioRecord ID does not exist | +| `RECORD_SCHEMA_INVALID` | sdk, api | Record does not conform to BSP schema | +| `BIOMARKER_CODE_UNKNOWN` | sdk | Biomarker code not in taxonomy | +| `CATEGORY_CODE_UNKNOWN` | sdk | Category code not in taxonomy | +| `UNIT_MISMATCH` | sdk | Unit does not match expected unit for biomarker | +| `VALUE_OUT_OF_RANGE` | sdk | Value outside physiologically plausible range | +| `ARWEAVE_PERSIST_FAILED` | api | Record was validated but could not be persisted to Arweave | +| `ARWEAVE_TX_NOT_CONFIRMED` | api | Arweave TX not yet confirmed at read time | + +--- + +## Relayer / Network + +| Code | Source | Meaning | +|---|---|---| +| `RELAYER_UNREACHABLE` | sdk, cli, mcp | Cannot connect to the registry endpoint | +| `RELAYER_GAS_FAILED` | api | Relayer could not submit Aptos transaction (insufficient gas) | +| `NETWORK_MISMATCH` | sdk | Payload network (mainnet/testnet) does not match relayer | +| `RATE_LIMITED` | api | Too many requests from this origin | +| `CHAIN_TX_FAILED` | api | Aptos transaction reverted | +| `CHAIN_TIMEOUT` | api | Aptos transaction did not confirm within window | + +--- + +## Configuration / Client + +| Code | Source | Meaning | +|---|---|---| +| `PRIVATE_KEY_MISSING` | cli, sdk | No private key configured | +| `PRIVATE_KEY_INVALID` | cli, sdk | Private key not a valid 128-char hex Ed25519 key | +| `CONFIG_NOT_FOUND` | cli | Config file does not exist at `~/.bsp/config.json` | +| `CONFIRM_REQUIRED` | cli | Destructive operation invoked without `--confirm` | +| `ENV_VAR_MISSING` | mcp | Required env var not set (e.g. `BSP_BEO_DOMAIN`) | + +--- + +## Cross-reference + +| Canonical source | Location | +|---|---| +| **Contract errors** | `ambrosio-institute/bsp-contracts` → `sources/errors.move` | +| **API errors** | `ambrosio-institute/bsp-registry-api` → `src/errors/catalog.ts` | +| **SDK errors** | `bsp-sdk-typescript` → `src/errors.ts` / `bsp-sdk-python` → `bsp_sdk/errors.py` | +| **CLI errors** | `bsp-cli` → `src/lib/errors.ts` | +| **MCP errors** | `bsp-mcp` → `src/index.ts` (ConsentGuard + tool handlers) | + +--- + +## Stability guarantee + +- Error codes are **append-only** within a major version. +- A code MAY change its `message` without notice. Never parse messages. +- Deprecated codes will be announced one minor version before removal. + +## Related + +- **Architecture** — `docs/ARCHITECTURE.md` +- **Glossary** — `docs/GLOSSARY.md` +- **Governance** — `spec/governance.md` diff --git a/docs/GLOSSARY.md b/docs/GLOSSARY.md new file mode 100644 index 0000000..620c6ae --- /dev/null +++ b/docs/GLOSSARY.md @@ -0,0 +1,138 @@ +# BSP — Glossary + +> Canonical definitions of every term used across the BSP specification, SDK, CLI, MCP server, and website. +> Terms are normative — implementations MUST use these meanings. + +--- + +## Core Entities + +### BEO — Biological Entity Object +The sovereign biological identity of a human being. A BEO is the on-chain anchor that ties together a person's public key, `.bsp` domain, recovery configuration, and status. One human → one BEO. Lifelong. Non-transferable. Stored as an Aptos Move resource. + +**Fields:** `beoId`, `domain`, `publicKey`, `keyVersion`, `status` (`ACTIVE` | `LOCKED` | `DESTROYED`), `recoveryConfig`, `createdAt`. + +**Spec:** `spec/beo.md` + +--- + +### IEO — Institutional Entity Object +The on-chain identity of an institution that interacts with BEOs — laboratories, hospitals, wearable companies, physicians, insurers, research institutions, and platforms. An IEO must be registered to issue or receive records. Every BioRecord is signed by an IEO. + +**Types:** `LAB`, `HOSPITAL`, `WEARABLE`, `PHYSICIAN`, `INSURER`, `RESEARCH`, `PLATFORM`. + +**Fields:** `ieoId`, `domain`, `type`, `name`, `publicKey`, `certificationLevel`, `status`, `createdAt`. + +**Spec:** `spec/ieo.md` + +--- + +### BioRecord +A single biological measurement expressed in the BSP data schema. Every biomarker value — from a blood test to a wearable reading — is a BioRecord. BioRecords are permanent, signed by the issuing IEO, and stored on Arweave with a pointer on-chain. + +**Required fields:** `recordId`, `beoId`, `ieoId`, `categoryCode` (e.g. `BSP-LA`), `biomarkerCode`, `value`, `unit`, `referenceRange`, `collectedAt`, `issuedAt`, `signature`, `arweaveTxId`. + +**Spec:** `spec/biorecord.md` + +--- + +### ConsentToken +A cryptographically signed on-chain authorization issued by a BEO holder to an IEO. Every data access in BSP is gated by a ConsentToken. The token defines exactly what the IEO may do, for how long, and over which data categories. + +**Fields:** `tokenId`, `beoId`, `ieoId`, `intents[]`, `categories[]` (optional), `expiresAt` (optional), `issuedAt`, `revokedAt` (nullable), `status`. + +**Lifecycle:** issued → active → (optional) revoked / expired. + +--- + +## Consent Model + +### Scope +The set of data categories a ConsentToken covers. Represented as an array of BSP category codes (e.g. `BSP-LA`, `BSP-CV`, `BSP-HM`). A token with scope `[BSP-LA, BSP-HM]` cannot be used to read `BSP-CV` data even if that category exists in the BEO. If `categories` is empty or omitted, the token grants access to **all** categories under the authorized intents. + +--- + +### Intent +The specific action a ConsentToken authorizes. Intents are discrete and additive — a single token may carry multiple intents. + +**Enumerated intents:** + +| Intent | Meaning | +|---|---| +| `SUBMIT_RECORD` | IEO may submit new BioRecords to the BEO | +| `READ_RECORDS` | IEO may read existing BioRecords | +| `ANALYZE_VITALITY` | IEO may compute derived analyses (never modifies records) | +| `REQUEST_SCORE` | IEO may request longevity / vitality scores | +| `EXPORT_DATA` | IEO may export data in FHIR / JSON / CSV on behalf of holder | +| `SYNC_PROTOCOL` | IEO may synchronize protocol updates (e.g. wearable sync) | + +--- + +### Domain (`.bsp`) +The human-readable BSP identifier. A `.bsp` domain is cryptographically bound to a BEO (for humans) or an IEO (for institutions). Examples: `alice.bsp`, `fleury.bsp`. Resolves via the Aptos contract, not via DNS. Unique. Released when the BEO is destroyed. + +**Spec:** `spec/bsp-domain.md` + +--- + +### Guardian +A designated party pre-authorized to initiate BEO recovery if the holder loses access to their private key. Guardians are defined at BEO creation time in the `recoveryConfig` and stored on-chain as public keys. Recovery typically requires an N-of-M multi-signature from guardians (configurable). + +--- + +### Recovery +The process of restoring access to a BEO when the holder has lost their private key. Recovery rotates the BEO's public key (incrementing `keyVersion`) after the configured guardian threshold signs the recovery payload. Recovery is **not** erasure — all existing BioRecords and ConsentTokens continue unchanged. + +--- + +### Relayer +A service that receives signed BSP payloads from clients and submits them as Aptos transactions, paying the gas. The relayer is trustless from the protocol's perspective: it cannot forge signatures, modify payloads, or produce state the chain would not otherwise accept. Any party can run a relayer. The official relayer is the Registry API. + +--- + +## Operational Terms + +### Cryptographic Erasure +The protocol-native implementation of LGPD Art. 18 (Brazil) and GDPR Art. 17 (EU) "right to erasure". When a BEO is destroyed, its public key is nullified on-chain. Arweave records remain permanently stored but are no longer associated with a verifiable identity — effectively unreadable. All ConsentTokens are simultaneously revoked and the `.bsp` domain is released. + +--- + +### Nonce +A unique value (minimum 16 characters) included in every signed payload to prevent replay attacks. Combined with a timestamp (max 5 minutes old), the chain rejects any payload that has been seen before. + +--- + +### Key Version +A monotonically increasing counter tracking how many times a BEO's public key has been rotated. Starts at `1`. Increments on every `rotate-key` and on recovery. Old signatures remain valid for past records (verified against historic key versions). + +--- + +### BIP — BSP Improvement Proposal +The formal governance mechanism for changing the protocol. Anyone can draft a BIP. Accepted BIPs become part of the canonical specification. Process defined in `spec/governance.md` and `bip/BIP-0000-template.md`. + +--- + +## Taxonomy + +### Category Code +A short identifier for a family of biomarkers (e.g. `BSP-LA` for Lab Advanced, `BSP-CV` for Cardiovascular, `BSP-HM` for Hematology). 25 categories across 4 levels. + +### Biomarker Code +A unique identifier for a single measurable biomarker within a category. Format `BSP-{category}-{marker}`. Example: `BSP-HM-HGB` for hemoglobin. + +### Taxonomy Levels +- **Level 1 — Core** (9 categories): advanced longevity biomarkers +- **Level 2 — Standard** (9 categories): routine laboratory biomarkers +- **Level 3 — Extended** (6 categories): specialized biomarkers +- **Level 4 — Device** (1 category): continuous wearable data + +**Spec:** `spec/taxonomy/` + +--- + +## Related + +- **Architecture** — `docs/ARCHITECTURE.md` +- **Error Codes** — `docs/ERROR_CODES.md` +- **Implementation Guide** — `docs/implementation-guide.md` +- **Website Glossary** — `https://biologicalsovereigntyprotocol.com/glossary` From f5c0adb84ab0a900199a7043991a0efc30c54505 Mon Sep 17 00:00:00 2001 From: Andre Ambrosio Date: Mon, 20 Apr 2026 12:09:54 -0300 Subject: [PATCH 3/5] docs(ops): publish public SLA v1.0 - docs/SLA.md: 99.5% monthly uptime target for Registry API, P95 <500ms for reads and <3s for writes, maintenance windows, incident classification, communication channels, data integrity guarantees, SLA breach reporting procedure. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/SLA.md | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 docs/SLA.md diff --git a/docs/SLA.md b/docs/SLA.md new file mode 100644 index 0000000..eb6b6dd --- /dev/null +++ b/docs/SLA.md @@ -0,0 +1,112 @@ +# BSP Service Level Agreement + +Public SLA for the Biological Sovereignty Protocol (BSP) reference services operated by the Ambrosio Institute. Defines the commitments on availability, latency, and communication between the Institute and BSP users (BEOs, Guardians, Keyholders, integrators). + +> **Version**: 1.0 — published 2026-04-20. +> **Scope**: applies to production (`mainnet`) services only. Testnet environments are best-effort with no SLA. + +## 1. Services Covered + +| Service | Endpoint | Notes | +|---------|----------|-------| +| Registry API | `https://registry.biologicalsovereigntyprotocol.com` | Core read/write API for BEO identity, tokens, biorecords. | +| Identity Web | `https://id.biologicalsovereigntyprotocol.com` | User-facing BSP ID management. | +| Status Page | `https://status.biologicalsovereigntyprotocol.com` | Incident and maintenance communication. | +| Contracts | Aptos mainnet package at `` | On-chain state is final and permanent by protocol design. | + +External dependencies **not under our control** (best-effort only): +- Aptos mainnet availability (Aptos Foundation SLA). +- Arweave network availability. +- `arweave.net` gateway availability. + +## 2. Availability Commitment + +**99.5% monthly uptime** on the Registry API, measured as: + +``` +uptime = (total_minutes_in_month - downtime_minutes) / total_minutes_in_month +``` + +Downtime = cumulative minutes where synthetic probes against `/health` fail from at least 2 of 3 geographic monitoring regions. + +Excluded from downtime: +- Scheduled maintenance announced > 72 hours in advance (up to 4 hours per month). +- Outage caused by external dependencies listed above when confirmed by their status pages. +- Force majeure (natural disaster, state action, internet-scale routing failure). +- Abuse mitigation or security incident response where exclusion is necessary to protect user data. + +### 2.1 Service Credits + +Not applicable at this phase (pre-commercial). The Institute publishes monthly uptime reports as a transparency commitment. Commercial SLA with credits is available to Institute Partner tier subscribers (out of scope of this document). + +## 3. Latency Commitments + +Measured on the Registry API at the 95th percentile over a 5-minute sliding window, from the same region as the operator. + +| Operation Class | Examples | P95 Target | +|-----------------|----------|------------| +| **Read** (cached / no Aptos call) | `GET /v1/verify/:token`, `GET /v1/beo/:id/public` | **< 500 ms** | +| **Read** (on-chain, cache miss) | `GET /v1/beo/:id` first call | < 1.5 s | +| **Write** (Aptos tx required) | `POST /v1/tokens/issue`, `POST /v1/beo/rotate_key`, `POST /v1/biorecord/create` | **< 3 s** (includes Aptos finality) | +| **Write** (Arweave + Aptos) | Biorecord with payload upload | < 8 s | +| **Admin / governance** | Institute-only endpoints | Best-effort | + +Latency SLA is suspended during an active dependency incident (Aptos or Arweave) provided such incident is declared on our status page. + +## 4. Maintenance Windows + +- **Preferred window**: Sunday 02:00–06:00 UTC (low global traffic). +- **Announcement**: at least 72 hours in advance via status page + email to registered integrators. +- **Budget**: up to 4 hours of scheduled downtime per calendar month, excluded from the uptime calculation. +- **Emergency maintenance**: may occur without advance notice when a security or data-integrity issue is being mitigated. Announced in real time on status page. + +## 5. Communication Channels + +| Purpose | Channel | +|---------|---------| +| Real-time service status | https://status.biologicalsovereigntyprotocol.com | +| Incident reports (post-mortem) | https://biologicalsovereigntyprotocol.com/incidents | +| Release notes | https://biologicalsovereigntyprotocol.com/releases | +| Security advisories | https://biologicalsovereigntyprotocol.com/security | +| Operator contact | `ops@biologicalsovereigntyprotocol.com` | +| Security vulnerability reports | `security@biologicalsovereigntyprotocol.com` (PGP key on SECURITY.md) | + +> **Status page placeholder**: the `status.*` subdomain is currently being provisioned. During the interim, incidents are announced at the main domain `/status` path and cross-posted to the Ambrosio Institute GitHub org announcements. + +## 6. Incident Classification (as exposed to users) + +Matches our internal runbooks. User-visible severities: + +| Level | Definition | Example user impact | +|-------|------------|---------------------| +| **Outage** | Service unavailable. | Cannot authenticate, cannot issue tokens. | +| **Degraded** | Slower than SLA or partial feature failure. | Biorecord uploads slow; read paths OK. | +| **Informational** | Maintenance, upcoming change, advisory. | Scheduled upgrade at T+48h. | + +Each incident receives a dedicated status-page entry with timestamped updates. Post-mortems for "Outage" incidents are published within 7 calendar days. + +## 7. Data Integrity Guarantees + +BSP's design makes several properties protocol-level (not dependent on this SLA): + +- **On-chain state** (Aptos): append-only, final after Aptos finality. No loss possible while Aptos operates. +- **Biorecord payloads** (Arweave): immutable once confirmed. Hash proofs allow third-party verification. +- **User keys**: held by users (hardware wallets or secure enclave); the Institute cannot access or move funds. +- **No silent mutation**: every state change produces a verifiable on-chain event. + +The Registry API is a thin relay layer over these guarantees. Even if the API is entirely destroyed, user data remains accessible via direct Aptos and Arweave queries. + +## 8. Changes to This SLA + +- Material changes are announced ≥ 30 days in advance via the release notes channel. +- Minor clarifications may be applied retroactively; diff is visible via Git history of this file. +- Current version is always at `bsp-spec/docs/SLA.md` on the `main` branch. + +## 9. Reporting a Breach + +If you believe the Institute has breached this SLA: +1. Email `ops@biologicalsovereigntyprotocol.com` with subject `SLA BREACH — `. +2. Include: timeframe, affected endpoints, evidence (probe logs, screenshots), impact on you. +3. Acknowledgement within 2 business days. Resolution within 10 business days. + +The Institute commits to publishing a transparency report each quarter summarizing SLA performance, incidents, and resolutions. From 0e0a6fa76ac4e68e697dd9881b6fd28f27713e51 Mon Sep 17 00:00:00 2001 From: Andre Ambrosio Date: Mon, 20 Apr 2026 12:03:13 -0300 Subject: [PATCH 4/5] docs(errors): consolidate ERROR_CODES as single source of truth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merge contracts (78 abort_codes) + API (31 machine codes) + SDK/CLI/MCP surfaces into one canonical catalog. Downstream repos will link here instead of duplicating tables. Structure: - Part I — Protocol-level string codes (SDK/CLI/MCP) - Part II — Registry API HTTP + machine codes - Part III — Move abort_code ranges per module - Part IV — SDK / CLI / MCP surface notes Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/ERROR_CODES.md | 350 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 325 insertions(+), 25 deletions(-) diff --git a/docs/ERROR_CODES.md b/docs/ERROR_CODES.md index e74a006..29a8501 100644 --- a/docs/ERROR_CODES.md +++ b/docs/ERROR_CODES.md @@ -1,7 +1,10 @@ -# BSP — Error Codes +# BSP — Error Codes (Single Source of Truth) -> Canonical error catalog. Every BSP implementation (contracts, API, SDK, CLI, MCP server) MUST emit these codes on failure. -> Codes are stable — they will not be renamed across minor versions. +> **Canonical error catalog for the entire BSP stack.** +> +> Every BSP implementation — Move contracts, Registry API, SDKs (TypeScript & Python), CLI, and MCP server — MUST emit the codes defined here. +> +> This document is the **single source of truth**. Downstream repos (`bsp-contracts`, `bsp-registry-api`, `bsp-sdk-typescript`, `bsp-sdk-python`, `bsp-cli`, `bsp-mcp`) link to this file and do **not** duplicate the catalog. --- @@ -20,6 +23,19 @@ Clients MUST NOT parse error messages. Always branch on `code`. --- +## Stability guarantee + +- Error codes are **append-only** within a major version. +- A code MAY change its `message` without notice. Never parse messages. +- Deprecated codes will be announced one minor version before removal. +- Numeric `abort_code` values in Move modules are stable — ranges documented below are reserved per module and MUST NOT collide. + +--- + +# Part I — High-level Protocol Codes + +Protocol-level identifiers used by SDKs, CLI, MCP, and public APIs. These are string codes, stable across implementations. + ## Identity (BEO / IEO) | Code | Source | Meaning | @@ -35,8 +51,6 @@ Clients MUST NOT parse error messages. Always branch on `code`. | `IEO_TYPE_INVALID` | sdk | IEO type not in enum (`LAB`, `HOSPITAL`, `WEARABLE`, ...) | | `KEY_VERSION_MISMATCH` | contract | Signature was produced with an outdated key version | ---- - ## Consent | Code | Source | Meaning | @@ -49,8 +63,6 @@ Clients MUST NOT parse error messages. Always branch on `code`. | `SCOPE_VIOLATION` | contract | Requested category is outside token.categories | | `TOKEN_SIGNATURE_INVALID` | contract | Signature on token payload does not match BEO public key | ---- - ## Cryptography / Signatures | Code | Source | Meaning | @@ -63,8 +75,6 @@ Clients MUST NOT parse error messages. Always branch on `code`. | `TIMESTAMP_IN_FUTURE` | contract | Request timestamp beyond acceptable skew | | `PUBLIC_KEY_NULLIFIED` | contract | BEO key has been nullified (destroyed BEO) | ---- - ## BioRecord / Data | Code | Source | Meaning | @@ -78,8 +88,6 @@ Clients MUST NOT parse error messages. Always branch on `code`. | `ARWEAVE_PERSIST_FAILED` | api | Record was validated but could not be persisted to Arweave | | `ARWEAVE_TX_NOT_CONFIRMED` | api | Arweave TX not yet confirmed at read time | ---- - ## Relayer / Network | Code | Source | Meaning | @@ -91,8 +99,6 @@ Clients MUST NOT parse error messages. Always branch on `code`. | `CHAIN_TX_FAILED` | api | Aptos transaction reverted | | `CHAIN_TIMEOUT` | api | Aptos transaction did not confirm within window | ---- - ## Configuration / Client | Code | Source | Meaning | @@ -105,23 +111,317 @@ Clients MUST NOT parse error messages. Always branch on `code`. --- -## Cross-reference +# Part II — Registry API HTTP Codes -| Canonical source | Location | -|---|---| -| **Contract errors** | `ambrosio-institute/bsp-contracts` → `sources/errors.move` | -| **API errors** | `ambrosio-institute/bsp-registry-api` → `src/errors/catalog.ts` | -| **SDK errors** | `bsp-sdk-typescript` → `src/errors.ts` / `bsp-sdk-python` → `bsp_sdk/errors.py` | -| **CLI errors** | `bsp-cli` → `src/lib/errors.ts` | -| **MCP errors** | `bsp-mcp` → `src/index.ts` (ConsentGuard + tool handlers) | +HTTP semantics and machine codes for `bsp-registry-api`. All error responses follow: + +```json +{ "error": "Human-readable message", "code": "OPTIONAL_MACHINE_CODE" } +``` + +Some consent-verification endpoints return `{ "valid": false, "reason": "..." }` inside a 200 OK — those are domain reasons, not HTTP errors. + +## HTTP Status Codes + +| Status | Meaning | When it happens | +|---|---|---| +| **200 OK** | Success | Request completed. For verify/cert endpoints, check `valid`/`certified` field. | +| **201 Created** | Resource created | BEO/IEO/ConsentToken successfully written on-chain. | +| **400 Bad Request** | Validation error | Missing/invalid field, malformed nonce/timestamp, schema mismatch. | +| **401 Unauthorized** | Auth failed | Invalid Ed25519 signature, invalid/expired JWT, revoked/expired consent token. | +| **403 Forbidden** | Not authorized | Consent token valid but intent/category not in scope. Institute-only endpoints without API key. | +| **404 Not Found** | Resource missing | BEO, IEO, or ConsentToken does not exist on-chain. | +| **409 Conflict** | Nonce reuse | `nonce` already consumed (replay protection). | +| **422 Unprocessable Entity** | Business rule | e.g. `TOO_MANY_RECORDS` — payload schema is valid but violates a policy. | +| **429 Too Many Requests** | Rate limited | Per-route quotas exceeded (10–60 req/min depending on route group). | +| **500 Internal Server Error** | Unexpected failure | Uncaught exception. Logs contain the correlation id. | +| **503 Service Unavailable** | Upstream flaky | Aptos RPC circuit breaker is open — retry in ~30s. | + +## API Machine Codes + +### Authentication / Signatures + +| Code | HTTP | Description | Action | +|---|---|---|---| +| `INVALID_SIGNATURE` | 401 | Ed25519 verification failed against the BEO/IEO on-chain public key. | Re-sign the canonical payload with the correct key; verify nonce+timestamp are echoed back. | +| `TOKEN_NOT_FOUND` | 401 | ConsentToken id not present on-chain. | Check the token was created and the id is correct. | +| `TOKEN_REVOKED` | 401 | Token was revoked by the BEO holder. | Request a fresh ConsentToken. | +| `TOKEN_EXPIRED` | 401 | Token `expires_at` is past. | Request a fresh ConsentToken with a new TTL. | +| `INTENT_NOT_AUTHORIZED` | 403 | Required intent (`SUBMIT_RECORD`, `READ_RECORDS`, `EXPORT_DATA`) not in token scope. | Ask the holder to grant a new token with the required intent. | +| `IEO_NOT_FOUND` | 401 | IEO referenced by the token no longer exists / is destroyed. | Re-issue from a live IEO. | + +### Consent Exchange + +| Code | HTTP | Description | Action | +|---|---|---|---| +| `RECORDS_REQUIRED` | 400 | `records` must be a non-empty array. | Send at least one record. | +| `TOO_MANY_RECORDS` | 422 | More than 50 records in a single `submitRecords` call. | Split into multiple batches of <=50. | +| `ENCRYPTION_SECRET_MISSING` | 500 | Server was not configured with `BIOSTORE_ENCRYPTION_SECRET` or `JWT_SECRET`. | Operator: set the env var and restart. | + +### Nonce / Replay Protection + +| Code | HTTP | Description | Action | +|---|---|---|---| +| `NONCE_REQUIRED` | 400 | Missing `nonce` field. | Send a fresh >=16-char random nonce. | +| `NONCE_TOO_SHORT` | 400 | Nonce has fewer than 16 characters. | Use a >=16-char cryptographic random nonce. | +| `NONCE_REUSED` | 409 | Nonce was already seen (replay detected). | Generate a new nonce per request. | +| `TIMESTAMP_SKEW` | 400 | Timestamp more than 5 minutes from server clock. | Sync client clock (NTP) and retry. | + +### Guardians + +| Code | HTTP | Description | Action | +|---|---|---|---| +| `JWT_INVALID` | 401 | Invitation/recovery JWT signature invalid. | Request a new invitation. | +| `JWT_EXPIRED` | 401 | JWT past 72h TTL. | Ask the BEO holder to resend the invitation. | +| `INVITATION_REPLAYED` | 401 | Invitation JWT was already accepted. | A guardian can only accept once. | + +### Arweave + +| Code | HTTP | Description | Action | +|---|---|---|---| +| `ARWEAVE_UPLOAD_FAILED` | 500 | Upload rejected after 3 retries (exponential backoff 1s->4s). | Retry later; if persistent, check `ARWEAVE_WALLET_KEY` balance. | +| `ARWEAVE_TXID_INVALID` | 400 | Referenced Arweave tx id is malformed. | Verify the id is 43 base64url chars. | + +### Aptos / Circuit Breaker + +| Code | HTTP | Description | Action | +|---|---|---|---| +| `APTOS_RPC_UNAVAILABLE` | 503 | Circuit breaker is OPEN after >50% error rate over the rolling window. | Retry after ~30s — the breaker will half-open automatically. | +| `APTOS_SUBMIT_FAILED` | 500 | Transaction submit/wait errored (non-retryable path). | Inspect logs; verify relayer account has APT for gas. | + +### Institute / Admin + +| Code | HTTP | Description | Action | +|---|---|---|---| +| `INSTITUTE_API_KEY_MISSING` | 401 | `X-Institute-Api-Key` header absent. | Provide the key (admin only). | +| `INSTITUTE_API_KEY_INVALID` | 401 | Provided key does not match `INSTITUTE_API_KEY`. | Confirm the key; rotate if compromised. | + +## API Handling Guidance + +- **Retries:** Safe to retry on `5xx` and `APTOS_RPC_UNAVAILABLE`. Never retry on `4xx` without changing the payload (nonce must be fresh). +- **Correlation id:** Every response includes `X-Request-Id`; log it and include it in support tickets. +- **Backoff:** When you hit `429` or `503`, honor `Retry-After` when present; otherwise exponential backoff with jitter (1s, 2s, 4s, cap at 30s). +- **Consent verification:** `GET /api/consent/:tokenId` returns **200** with `{ valid: false, reason }` for domain-level failures. Only transport/server errors surface as 4xx/5xx here. --- -## Stability guarantee +# Part III — Move Contract Abort Codes + +Numeric `abort_code` values emitted by each Move module in `bsp-contracts/src/move/sources/`. SDKs and relayers MUST map `vm_status.abort_code` back to these constants by module range. + +## Code ranges per module + +| Range | Module | File | +|-------|--------|------| +| 1 – 99 | `beo_registry` | `src/move/sources/beo_registry.move` | +| 100 – 199 | `ieo_registry` | `src/move/sources/ieo_registry.move` | +| 200 – 299 | `domain_registry` | `src/move/sources/domain_registry.move` | +| 300 – 399 | `access_control` | `src/move/sources/access_control.move` | +| 400 – 499 | `governance` | `src/move/sources/governance.move` | + +## `beo_registry` (1 – 99) + +| Código | Constante | Descrição | Como tratar | +|--------|-----------|-----------|-------------| +| 1 | `E_BEO_NOT_FOUND` | BEO id inexistente | Checar `beo_id`; oferecer criar BEO novo | +| 2 | `E_BEO_DESTROYED` | BEO foi destruído (LGPD) | Estado terminal — criar novo BEO | +| 3 | `E_BEO_LOCKED` | BEO bloqueado (emergência) | Pedir unlock ou recovery | +| 4 | `E_DOMAIN_REQUIRED` | Domínio vazio | Preencher domínio `.bsp` | +| 5 | `E_DOMAIN_TOO_LONG` | Domínio acima do limite | Reduzir tamanho (<64 chars) | +| 6 | `E_DOMAIN_INVALID_SUFFIX` | Sufixo não é `.bsp` | Usar sufixo `.bsp` | +| 7 | `E_DOMAIN_INVALID_FORMAT` | Caracteres inválidos | Apenas `[a-z0-9-]` | +| 8 | `E_DOMAIN_TAKEN` | Domínio já registrado | Escolher outro domínio | +| 9 | `E_PUBLIC_KEY_TAKEN` | Pubkey já usada | Gerar novo par de chaves | +| 10 | `E_FIELD_REQUIRED` | Campo obrigatório ausente | Validar input antes do envio | +| 11 | `E_INVALID_SIGNATURE` | Assinatura Ed25519 inválida | Conferir chave + mensagem canônica | +| 12 | `E_TIMESTAMP_TOO_OLD` | Timestamp expirado (>5min) | Reassinar com `now()` | +| 13 | `E_TIMESTAMP_FUTURE` | Timestamp no futuro | Conferir clock do cliente | +| 14 | `E_RECOVERY_NO_CONFIG` | Nenhum guardião configurado | `update_recovery` primeiro | +| 15 | `E_RECOVERY_IN_PROGRESS` | Recovery já em andamento | Aguardar expiração ou conclusão | +| 16 | `E_RECOVERY_COOLDOWN` | Cooldown de 24h ativo | Aguardar janela de cooldown | +| 17 | `E_RECOVERY_EXPIRED` | Janela de 72h expirou | Reiniciar recovery | +| 18 | `E_GUARDIAN_NOT_FOUND` | Contato não é guardião | Confirmar lista de guardians | +| 19 | `E_GUARDIAN_ALREADY_ACTIVE` | Guardião já aceitou | Nada a fazer | +| 20 | `E_GUARDIAN_NO_KEY` | Guardião sem pubkey | Guardião precisa aceitar primeiro | +| 21 | `E_GUARDIAN_ALREADY_CONFIRMED` | Guardião já confirmou recovery | Usar outro guardião | +| 22 | `E_KEY_SAME` | Nova chave igual à antiga | Gerar chave diferente | +| 23 | `E_KEY_INVALID_LENGTH` | Pubkey com tamanho inválido | Usar chave Ed25519 de 32 bytes | +| 24 | `E_RECOVERY_GUARDIANS_MIN` | <2 guardiões | Adicionar mais guardiões | +| 25 | `E_RECOVERY_THRESHOLD_INVALID` | Threshold fora de range | `1 <= threshold <= len(guardians)` | +| 26 | `E_GUARDIAN_ROLE_INVALID` | Role fora de `{0,1,2}` | Usar PRIMARY/SECONDARY/TERTIARY | +| 27 | `E_GUARDIAN_DUPLICATE_CONTACT` | Mesmo contato duas vezes | Contatos devem ser únicos | +| 28 | `E_ALREADY_LOCKED` | BEO já bloqueado | Nada a fazer | +| 29 | `E_NOT_LOCKED` | BEO não está bloqueado | Nada a fazer | +| 30 | `E_ALREADY_DESTROYED` | BEO já destruído | Estado terminal | +| 31 | `E_NOT_INITIALIZED` | Módulo não inicializado | Chamar `initialize` primeiro | +| 32 | `E_ALREADY_INITIALIZED` | Módulo já inicializado | Uma inicialização apenas | + +## `ieo_registry` (100 – 199) + +| Código | Constante | Descrição | Como tratar | +|--------|-----------|-----------|-------------| +| 100 | `E_IEO_NOT_FOUND` | IEO inexistente | Checar `ieo_id` | +| 101 | `E_IEO_DESTROYED` | IEO destruído | Estado terminal | +| 102 | `E_IEO_LOCKED` | IEO bloqueado | Unlock via governance | +| 103 | `E_IEO_SUSPENDED` | IEO suspenso por compliance | Resolver compliance | +| 104 | `E_IEO_REVOKED` | IEO revogado | Criar novo IEO | +| 105 | `E_DOMAIN_REQUIRED` | Domínio vazio | Preencher domínio | +| 106 | `E_DOMAIN_TOO_LONG` | Domínio muito longo | Reduzir tamanho | +| 107 | `E_DOMAIN_TAKEN` | Domínio já em uso | Escolher outro | +| 108 | `E_FIELD_REQUIRED` | Campo obrigatório ausente | Validar input | +| 109 | `E_INVALID_SIGNATURE` | Assinatura Ed25519 inválida | Reassinar com chave correta | +| 110 | `E_TIMESTAMP_TOO_OLD` | Timestamp expirado | Reassinar com `now()` | +| 111 | `E_TIMESTAMP_FUTURE` | Timestamp no futuro | Conferir clock | +| 112 | `E_NOT_KEYHOLDER` | Caller não é keyholder | Usar conta autorizada | +| 113 | `E_INVALID_IEO_TYPE` | Tipo IEO desconhecido | Usar tipo cadastrado | +| 114 | `E_INVALID_CERT_LEVEL` | Nível de certificação inválido | Consultar lista | +| 115 | `E_KEY_INVALID_LENGTH` | Pubkey tamanho inválido | Ed25519 32 bytes | +| 116 | `E_KEY_SAME` | Nova chave igual à antiga | Gerar chave diferente | +| 117 | `E_RECOVERY_NO_CONFIG` | Sem config de recovery | Configurar guardiões | +| 118 | `E_RECOVERY_IN_PROGRESS` | Recovery em andamento | Aguardar | +| 119 | `E_RECOVERY_COOLDOWN` | Cooldown ativo | Aguardar | +| 120 | `E_RECOVERY_EXPIRED` | Recovery expirado | Reiniciar | +| 121 | `E_GUARDIAN_NOT_FOUND` | Guardião não encontrado | Confirmar lista | +| 122 | `E_GUARDIAN_ALREADY_ACTIVE` | Guardião já ativo | Nada a fazer | +| 123 | `E_GUARDIAN_NO_KEY` | Guardião sem chave | Precisa aceitar | +| 124 | `E_GUARDIAN_ALREADY_CONFIRMED` | Guardião já confirmou | Usar outro | +| 125 | `E_RECOVERY_GUARDIANS_MIN` | <2 guardiões | Adicionar mais | +| 126 | `E_RECOVERY_THRESHOLD_INVALID` | Threshold fora de range | Corrigir | +| 127 | `E_GUARDIAN_ROLE_INVALID` | Role inválido | Usar PRIMARY/SECONDARY/TERTIARY | +| 128 | `E_GUARDIAN_DUPLICATE_CONTACT` | Contato duplicado | Contatos únicos | +| 129 | `E_ALREADY_LOCKED` | Já bloqueado | Nada a fazer | +| 130 | `E_NOT_LOCKED` | Não está bloqueado | Nada a fazer | +| 131 | `E_ALREADY_DESTROYED` | Já destruído | Estado terminal | +| 132 | `E_NOT_INITIALIZED` | Módulo não inicializado | Chamar `initialize` | +| 133 | `E_ALREADY_INITIALIZED` | Já inicializado | Uma vez apenas | +| 139 | `E_ALREADY_KEYHOLDER` | Já é keyholder | Nada a fazer | +| 140 | `E_LAST_KEYHOLDER` | Último keyholder, não pode remover | Adicionar outro primeiro | +| 141 | `E_IEO_TYPE_EXISTS` | Tipo IEO duplicado | Escolher outro nome | +| 142 | `E_IEO_TYPE_NOT_FOUND` | Tipo IEO não cadastrado | Registrar primeiro | +| 143 | `E_LAST_IEO_TYPE` | Último tipo IEO | Manter ao menos um | +| 144 | `E_CANNOT_DESTROY_REVOKED` | IEO revogado não pode destruir | Desrevogar primeiro | +| 145 | `E_NOT_GOVERNANCE` | Caller não é governance | Usar módulo governance | + +## `domain_registry` (200 – 299) + +| Código | Constante | Descrição | Como tratar | +|--------|-----------|-----------|-------------| +| 200 | `E_DOMAIN_REQUIRED` | Domínio vazio | Preencher | +| 201 | `E_DOMAIN_TOO_LONG` | Domínio muito longo | Reduzir | +| 202 | `E_DOMAIN_INVALID_SUFFIX` | Sufixo inválido | Usar `.bsp` | +| 203 | `E_DOMAIN_ALREADY_REGISTERED` | Já registrado | Escolher outro | +| 204 | `E_DOMAIN_NOT_FOUND` | Domínio não encontrado | Verificar spelling | +| 205 | `E_NOT_AUTHORIZED` | Caller não autorizado | Usar institute ou registry addr | +| 206 | `E_ENTITY_ID_REQUIRED` | entity_id=0 | Preencher id válido | +| 207 | `E_INVALID_ENTITY_TYPE` | Tipo inválido | `BEO` ou `IEO` | +| 208 | `E_BEO_NON_TRANSFERABLE` | BEO não transferível | Criar novo BEO | +| 209 | `E_NOT_INITIALIZED` | Não inicializado | `initialize` primeiro | +| 210 | `E_ALREADY_INITIALIZED` | Já inicializado | Uma vez apenas | + +## `access_control` (300 – 399) + +| Código | Constante | Descrição | Como tratar | +|--------|-----------|-----------|-------------| +| 300 | `E_TOKEN_NOT_FOUND` | ConsentToken inexistente | Emitir token | +| 301 | `E_TOKEN_REVOKED` | Token revogado | Emitir novo | +| 302 | `E_FIELD_REQUIRED` | Campo ausente | Validar input | +| 303 | `E_INVALID_SIGNATURE` | Assinatura inválida | Reassinar | +| 304 | `E_TIMESTAMP_TOO_OLD` | Timestamp velho | Usar `now()` | +| 305 | `E_TIMESTAMP_FUTURE` | Timestamp futuro | Conferir clock | +| 306 | `E_BEO_NOT_ACTIVE` | BEO inativo | Checar status do BEO | +| 307 | `E_IEO_NOT_ACTIVE` | IEO inativo | Checar status do IEO | +| 308 | `E_INVALID_INTENT` | Intent não cadastrado | Usar intent válido | +| 309 | `E_DUPLICATE_TOKEN` | Token duplicado | Usar token existente | +| 310 | `E_INVALID_EXPIRES` | `expires_at` inválido | Futuro ou 0 | +| 311 | `E_SCOPE_REQUIRED` | Scope vazio | Incluir ao menos 1 intent | +| 312 | `E_PERIOD_INVALID` | `period_from >= period_to` | Corrigir ordem | +| 313 | `E_NOT_AUTHORIZED` | Caller não autorizado | Institute ou holder | +| 314 | `E_NOT_INITIALIZED` | Módulo não inicializado | `initialize` primeiro | +| 315 | `E_ALREADY_INITIALIZED` | Já inicializado | Uma vez apenas | + +### Códigos de resultado de `verify_token` + +`verify_token` retorna `(valid: bool, reason: u8)` em vez de abortar. + +| Código | Constante | Significado | +|--------|-----------|-------------| +| 0 | `VERIFY_VALID` | Token válido | +| 1 | `VERIFY_TOKEN_NOT_FOUND` | Token não encontrado | +| 2 | `VERIFY_TOKEN_MISMATCH` | `ieo_id` não bate com o token | +| 3 | `VERIFY_TOKEN_REVOKED` | Token revogado | +| 4 | `VERIFY_TOKEN_EXPIRED` | Token expirado | +| 5 | `VERIFY_INTENT_NOT_AUTHORIZED` | Intent fora do scope | +| 6 | `VERIFY_CATEGORY_NOT_AUTHORIZED` | Categoria fora do scope | +| 7 | `VERIFY_PERIOD_OUT_OF_SCOPE` | Período fora do range | +| 8 | `VERIFY_IEO_NOT_ACTIVE` | IEO está inativo | + +## `governance` (400 – 499) + +| Código | Constante | Descrição | Como tratar | +|--------|-----------|-----------|-------------| +| 400 | `E_NOT_INITIALIZED` | Governance não inicializada | `initialize` primeiro | +| 401 | `E_ALREADY_INITIALIZED` | Já inicializada | Uma vez apenas | +| 402 | `E_NOT_KEYHOLDER` | Caller não é keyholder | Usar keyholder ativo | +| 403 | `E_PROPOSAL_NOT_FOUND` | Proposta inexistente | Checar `proposal_id` | +| 404 | `E_PROPOSAL_EXECUTED` | Proposta já executada | Criar nova | +| 405 | `E_PROPOSAL_EXPIRED` | Prazo vencido | Criar nova | +| 406 | `E_ALREADY_APPROVED` | Já aprovada pelo caller | Nada a fazer | +| 407 | `E_INVALID_PROPOSAL_ACTION` | Ação inválida | Checar tipo | + +## Como tratar abort_code no SDK / relayer + +1. Capture o `vm_status` do `UserTransaction` falho. +2. Extraia `abort_code` e identifique o módulo pela origem do erro. +3. Mapeie para a constante pelo range (1–99 = BEO, 100–199 = IEO, etc.). +4. Exiba a mensagem amigável da coluna "Descrição". +5. Execute a ação sugerida em "Como tratar". + +Para novos códigos, abra PR editando este arquivo junto com o módulo Move. As tabelas acima são a fonte única — a documentação em `bsp-contracts/docs/ERROR_CODES.md` apenas aponta para cá. -- Error codes are **append-only** within a major version. -- A code MAY change its `message` without notice. Never parse messages. -- Deprecated codes will be announced one minor version before removal. +--- + +# Part IV — SDK & Client Error Surfaces + +## TypeScript SDK (`bsp-sdk-typescript`) + +- Location: throws generic `Error` with the protocol `code` from Part I in the `message` or a `.code` property. +- Consumers SHOULD branch on the string code, not the message text. +- For contract-level failures, the SDK surfaces the Move `abort_code` from Part III wrapped with the corresponding protocol code (e.g. `BEO_NOT_FOUND`). + +## Python SDK (`bsp-sdk-python`) + +- Location: raises exceptions whose `args[0]` contains the protocol code from Part I. +- On HTTP errors, the SDK includes the machine code from Part II when returned by the API. + +## CLI (`bsp-cli`) + +- All errors printed to stderr with the protocol `code` prefix. +- Exit codes: + - `0` — success + - `1` — generic error + - `2` — validation / bad usage + - `3` — missing config / private key + - `4` — confirm required for destructive op + +## MCP Server (`bsp-mcp`) + +- Errors surfaced through MCP error channel with the protocol code as `code` and human message. +- `ConsentGuard` rejections use `CONSENT_REQUIRED`, `INTENT_NOT_AUTHORIZED`, `TOKEN_EXPIRED`, `TOKEN_REVOKED`. + +--- + +## Cross-reference — where each surface emits these codes + +| Canonical source | Location | +|---|---| +| **Contract abort codes** | `ambrosio-institute/bsp-contracts` → `src/move/sources/*.move` | +| **API machine codes** | `ambrosio-institute/bsp-registry-api` → `src/errors/catalog.ts` | +| **TypeScript SDK** | `bsp-sdk-typescript` → thrown from `src/**/*.ts` | +| **Python SDK** | `bsp-sdk-python` → raised from `bsp_sdk/**/*.py` | +| **CLI** | `bsp-cli` → `src/lib/errors.ts` | +| **MCP server** | `bsp-mcp` → `src/index.ts` (ConsentGuard + tool handlers) | + +--- ## Related From 66089a9684c8a55a621024d27e788103821b4f42 Mon Sep 17 00:00:00 2001 From: Andre Ambrosio Date: Mon, 20 Apr 2026 16:01:15 -0300 Subject: [PATCH 5/5] docs: emergency operations, intents catalog, and threat model - Gap C: add Emergency Operations section to spec/governance.md covering trigger conditions, ACTION_SUSPEND_IEO, 2-of-3 threshold, and reactivation process. - Gap D: create docs/INTENTS_CATALOG.md as authoritative reference for SUBMIT_RECORD, READ_RECORDS, EXPORT_DATA, and MANAGE_CONSENT. - Gap E: create docs/THREAT_MODEL.md with adversary models (network observer, compromised relayer, malicious IEO), trust assumptions, attack surfaces with mitigations, and explicit out-of-scope items. Co-Authored-By: Claude Sonnet 4.6 --- docs/INTENTS_CATALOG.md | 82 ++++++++++++++++++++++++++++++++ docs/THREAT_MODEL.md | 103 ++++++++++++++++++++++++++++++++++++++++ spec/governance.md | 45 ++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 docs/INTENTS_CATALOG.md create mode 100644 docs/THREAT_MODEL.md diff --git a/docs/INTENTS_CATALOG.md b/docs/INTENTS_CATALOG.md new file mode 100644 index 0000000..e302f5b --- /dev/null +++ b/docs/INTENTS_CATALOG.md @@ -0,0 +1,82 @@ +# BSP Intents Catalog + +> Version 0.2 | Ambrósio Institute + +This is the authoritative list of all BSP intents. Every ConsentToken must declare one or more intents from this list. Undeclared or unknown intents are rejected by the AccessControl smart contract. + +--- + +## SUBMIT_RECORD + +**Description:** Allows the token holder to submit BioRecords to the target BEO. + +**Who can use:** IEOs of type `LABORATORY`, `HOSPITAL`, `PHYSICIAN`, `WEARABLE`. + +**Data scope:** Write-only. The submitting IEO cannot read back the records it submitted — it only receives a confirmation receipt (`record_id` + `arweave_tx`). + +**Restrictions:** +- Token must also specify authorized `categories`. Records outside those categories are rejected. +- `WEARABLE` IEOs are limited to the `BSP-DV` (Device) category only. +- Submission is final — records can be superseded but not deleted by the IEO. + +--- + +## READ_RECORDS + +**Description:** Allows the token holder to read BioRecords from the target BEO. + +**Who can use:** IEOs of type `PHYSICIAN`, `PLATFORM`. `INSURER` may only receive aggregate anonymized output — never individual records. + +**Data scope:** Read-only. Returns BioRecords filtered by the categories declared in the ConsentToken. Records outside the declared categories are invisible to the requester. + +**Restrictions:** +- `WEARABLE` IEOs can never be granted this intent — the smart contract rejects any ConsentToken that combines `IEOType.WEARABLE` with `READ_RECORDS`. +- Tokens must be time-limited (no persistent `READ_RECORDS` tokens for `INSURER` type). +- Pagination is enforced server-side (max 100 records per request, `offset`-based). + +--- + +## EXPORT_DATA + +**Description:** Allows the token holder to perform a sovereign data export — a full structured dump of the BEO's records in a portable format. + +**Who can use:** The BEO holder themselves (via the Ambrósio OS or self-sovereign tools). No third-party IEO may be granted this intent. + +**Data scope:** All BioRecords in the BEO, across all categories. Formats: `JSON`, `CSV`, `FHIR_R4`. + +**Restrictions:** +- This intent is exclusively self-sovereign. ConsentTokens granting `EXPORT_DATA` to a third-party IEO are invalid and rejected on-chain. +- Each export is logged on Arweave as an audit event. +- Rate-limited to prevent abuse: maximum 10 exports per 24-hour window. + +--- + +## MANAGE_CONSENT + +**Description:** Allows the actor to list, inspect, and revoke ConsentTokens associated with a BEO. + +**Who can use:** The BEO holder only. Granted as part of the holder's self-sovereign administrative capabilities. + +**Data scope:** ConsentToken metadata (token_id, ieo_id, intents, categories, granted_at, expires_at, revoked status). Does not expose BioRecord content. + +**Restrictions:** +- Cannot be delegated to any IEO. +- Revocation via this intent is immediate and irreversible — the on-chain record is final. +- Listing returns all tokens (active, expired, revoked) so the holder has complete auditability. + +--- + +## Additional Intents (IEO-facing) + +The following intents are authorized for institutional actors and documented in full in `spec/ieo.md`: + +| Intent | Authorized IEO Types | Description | +|---|---|---| +| `REQUEST_CERTIFICATION` | All types | Apply for BSP certification | +| `ANALYZE_VITALITY` | `PLATFORM` | Submit records for AVA analysis | +| `REQUEST_SCORE` | `PLATFORM` | Request SVA score generation | +| `SUBMIT_BIP` | All types | Submit a BSP Improvement Proposal | + +--- + +*Ambrósio Institute · ambrosioinstitute.org · biologicalsovereigntyprotocol.com* diff --git a/docs/THREAT_MODEL.md b/docs/THREAT_MODEL.md new file mode 100644 index 0000000..14e60d4 --- /dev/null +++ b/docs/THREAT_MODEL.md @@ -0,0 +1,103 @@ +# BSP Threat Model + +> Version 0.2 | Ambrósio Institute + +--- + +## Adversary Models + +### 1. Network Observer (Passive) + +An adversary positioned on the network path between a client and the BSP Registry API — an ISP, cloud provider, or compromised router. + +**Capabilities:** Observe encrypted traffic metadata (source, destination, timing, payload size). Cannot read payload content over TLS. + +**Goal:** Correlate BEO holders with IEOs — infer which institutions a person uses, frequency of submissions. + +**Mitigation implemented:** +- All API communication requires TLS 1.2+. Plaintext is rejected. +- Request IDs and audit logs do not leak BEO holder identity to the observer (no PII in HTTP headers). +- ConsentToken IDs are opaque UUIDs — they reveal nothing about the holder without access to the smart contract state. + +**Residual risk:** Traffic analysis at scale (e.g. timing correlation between IEO submission spike and a known patient population) is out of scope for the protocol layer. + +--- + +### 2. Compromised Relayer + +An adversary who has gained control of a BSP Relayer node — the off-chain component that submits transactions to Aptos on behalf of clients. + +**Capabilities:** Can see all transaction payloads passing through the relayer. Can delay, drop, or attempt to replay transactions. Cannot forge signatures. + +**Goal:** Replay a past consent grant or data submission to re-use a previously authorized operation. Suppress a revocation to keep a token alive. + +**Mitigation implemented:** +- Every transaction carries a unique `nonce` (≥16 bytes, hex-encoded). Nonces are stored in Redis with a 10-minute TTL and rejected on replay. +- Timestamps are validated: requests older than 5 minutes or more than 30 seconds in the future are rejected. +- Revocations are idempotent on-chain and reflected immediately in the AccessControl contract. A delayed revocation succeeds — it may be slightly late but cannot be permanently suppressed. +- All transactions are cryptographically signed by the BEO holder's Ed25519 key. The relayer cannot forge or modify payloads without invalidating the signature. + +**Residual risk:** A compromised relayer can delay (not suppress) revocations within the nonce TTL window. Holders should use direct on-chain submission for time-critical revocations. + +--- + +### 3. Malicious IEO + +A certified institution acting outside its authorization — attempting to read data it was not consented to, or exceeding its declared intent scope. + +**Capabilities:** Holds valid API credentials and one or more legitimate ConsentTokens. Attempts to query data outside token scope, or submits requests after token expiry/revocation. + +**Goal:** Access BioRecord categories not covered by the consent token. Persist access after the holder revokes. + +**Mitigation implemented:** +- Scope enforcement is double-checked: at the API layer (schema validation rejects queries outside declared categories) and at the Aptos AccessControl contract (on-chain enforcement is authoritative). +- Token expiry and revocation are checked on every request against the live contract state — there is no cached "token still valid" assumption. +- `WEARABLE` IEO type is structurally prevented from receiving `READ_RECORDS` intent at the contract level. +- Audit logs of every exchange operation are written to Arweave permanently. Malicious access attempts leave an irrefutable trail. + +**Residual risk:** An IEO that has not yet been suspended can continue operating between the moment of detected misbehavior and the completion of the emergency suspension process (see `spec/governance.md` — Emergency Operations). + +--- + +## Trust Assumptions + +| Component | What we trust | Basis | +|---|---|---| +| Aptos validators | Honest majority of validators; BFT consensus is correct | Aptos network design; same assumption as all Aptos-based apps | +| Arweave permanence | Data written to Arweave is immutable and permanently retrievable | Arweave economic model and network history | +| Ed25519 security | Discrete log problem is hard; no feasible forgery without the private key | NIST / academic consensus as of BSP v0.2 | +| Redis nonce store | Redis instance is not compromised at the infrastructure level | Operational security of the BSP Registry deployment | +| TLS certificates | Certificate authorities for BSP API endpoints are trustworthy | Standard web PKI; same assumption as HTTPS everywhere | + +--- + +## Attack Surfaces + +| Surface | Attack vector | Mitigation | +|---|---|---| +| `POST /api/relayer/*` | Replay attack with captured signed payload | Nonce + timestamp validation; Redis atomic SETNX | +| `GET /api/exchange/records` | Unauthenticated listing of BioRecords | Schema validation requires `consentToken`, `signature`, `nonce`, and `timestamp` on every request | +| ConsentToken issuance | Token grant with forged holder signature | Ed25519 signature verified against on-chain BEO public key before any token is minted | +| Token revocation | Race condition — two revokes for the same token | Aptos Move resource model makes revocation atomic; second caller receives `E_ALREADY_REVOKED` | +| Governance contract | Single-actor suspension/parameter change | 2-of-3 multi-signature required; no unilateral action possible | +| IEO registration | Self-registration with false credentials | IEO creation is permissionless; certification (which unlocks higher access) requires Institute audit | + +--- + +## Out of Scope + +The following threat scenarios are explicitly not addressed by the BSP protocol layer. They may be addressed at the application or operational level by individual deployments. + +**Client-side compromise.** If the BEO holder's device is compromised (malware, physical access), an attacker with access to the private key can perform any action the holder can. The protocol cannot distinguish legitimate from coerced requests. Holders are advised to use hardware key storage. + +**Physical security.** Attacks on the data center or hardware running BSP Registry API nodes are outside protocol scope. Deployments must implement appropriate physical and cloud security controls. + +**Social engineering of the Institute.** A malicious actor convincing Institute keyholders to co-sign a fraudulent governance action is out of scope. This is addressed by Institute operational security and the 2-of-3 threshold that requires collusion. + +**Quantum adversaries.** Ed25519 is not post-quantum secure. A sufficiently advanced quantum computer could break holder signatures. Migration to post-quantum key schemes will be addressed in a future BIP when NIST PQC standards are finalized. + +**BSP taxonomy correctness.** The protocol enforces that submitted biomarker codes exist in the taxonomy, but does not verify that the biological measurements themselves are accurate or clinically valid. Data quality is the responsibility of the submitting IEO. + +--- + +*Ambrósio Institute · ambrosioinstitute.org · biologicalsovereigntyprotocol.com* diff --git a/spec/governance.md b/spec/governance.md index d8cbf49..50c27d7 100644 --- a/spec/governance.md +++ b/spec/governance.md @@ -71,6 +71,51 @@ This prevents unilateral changes — including by the Institute's founder. The p --- +## Emergency Operations + +Certain conditions require immediate protocol-level action outside the normal BIP review cycle. These operations are authorized by the multi-signature governance threshold and take effect on-chain without waiting for community review. + +### Trigger Conditions + +| Condition | Examples | +|---|---| +| Security incident | Active exploit draining BEO consent tokens, unauthorized mass data read | +| Key compromise | Institute keyholder private key leaked or suspected stolen | +| Critical bug | Smart contract logic error with potential for data loss or consent bypass | +| Regulatory emergency | Court order or regulatory injunction requiring immediate suspension | + +### Suspension Action + +To suspend an IEO or freeze exchange operations, submit a governance transaction using the `ACTION_SUSPEND_IEO` action type via the Governance contract. + +```typescript +GovernanceAction { + action_type: 'ACTION_SUSPEND_IEO' + target_ieo: string // ieo_id of the institution to suspend + reason: string // Mandatory — recorded on-chain permanently + duration: number | null // Seconds until auto-expiry, or null for indefinite + evidence: string // Arweave tx ID of the incident evidence package +} +``` + +For protocol-wide freezes (all exchange halted), use `ACTION_FREEZE_EXCHANGE`. This should be reserved for catastrophic scenarios only. + +### Approval Threshold + +Emergency actions require a **2-of-3 multi-signature** from the current Institute keyholders. This is the same threshold as critical parameter changes. No single person — including the Institute founder — can unilaterally trigger a suspension. + +If a keyholder is unavailable (e.g. the compromise is of their key), a **1-of-2 emergency override** is available with a mandatory 24-hour on-chain delay, after which any remaining keyholder can countermand the action. + +### Reactivation Process + +1. Incident confirmed resolved (internal post-mortem document published on Arweave). +2. Submit `ACTION_REINSTATE_IEO` (or `ACTION_UNFREEZE_EXCHANGE`) via Governance contract. +3. Same 2-of-3 multi-signature threshold applies. +4. A BIP documenting the incident and any spec-level mitigations is filed within 30 days and fast-tracked to `REVIEW` status. +5. Reinstated IEO status returns to `ACTIVE`. All suspended consent tokens that were valid at the time of suspension are automatically revalidated unless individually revoked during the suspension window. + +--- + ## BIP Index | BIP | Title | Status |