Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions docs/infrastructure/message-box-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
id: infra-message-box-server
title: 'Message-box Server'
kind: infra
version: '1.1.14'
last_updated: '2026-07-25'
last_verified: '2026-07-25'
version: '1.1.39'
last_updated: '2026-08-24'
last_verified: '2026-08-24'
review_cadence_days: 30
status: stable
tags: [messaging, overlay, store-and-forward, authentication]
Expand Down Expand Up @@ -70,6 +70,7 @@ all limits, shared state, BRC-105 pricing, memory evidence, and scaling guidance
| Variable | Required | Description |
| ------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------- |
| NODE_ENV | No | `development`, `staging`, or `production` |
| BSV_NETWORK | No | `mainnet`, `testnet`, `ttn`, or `teratestnet` (default `mainnet`) |
| PORT | No | HTTP/WebSocket port (default 8080; takes precedence) |
| HTTP_PORT | No | Compatibility port fallback |
| HOSTING_DOMAIN | No | Public domain for overlay advertisement (e.g., `http://localhost:8080`) |
Expand Down Expand Up @@ -154,10 +155,12 @@ Migrations tracked in `src/migrations/`:
- `2025-01-31-001-notification-permissions.ts` – Firebase notification permissions
- `2025-01-31-002-device-registrations.ts` – Device registration tracking
- `2026-07-26-001-message-permission-scope.ts` – Enforce one box-wide or sender-specific permission per scope
- `2026-07-26-002-list-query-indexes.ts` – Add list-query indexes for sender and recipient access paths
- `2026-08-04-001-resource-safety.ts` – Add message quota, retention, and resource-safety state

## Health checks

- `GET /health` reports process liveness without authentication.
- `GET /health` and `GET /healthz` report process liveness without authentication.
- `GET /ready` verifies database connectivity and returns a non-sensitive 503
response while dependencies are unavailable.
- Test an authenticated WebSocket handshake separately when live transport is
Expand Down
25 changes: 16 additions & 9 deletions docs/infrastructure/uhrp-server-basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
id: infra-uhrp-basic
title: 'UHRP Server (Basic)'
kind: infra
version: '0.1.8'
last_updated: '2026-07-25'
last_verified: '2026-07-25'
version: '0.1.32'
last_updated: '2026-08-24'
last_verified: '2026-08-24'
review_cadence_days: 30
status: beta
tags: [uhrp, storage, file-server, development, lightweight]
Expand Down Expand Up @@ -36,7 +36,7 @@ Clients PUT files with authentication, retrieve files via public GET, and query
| Type | Requirement |
| ----------------- | --------------------------------------------------------------------------------------------------- |
| Database | None; filesystem-based storage |
| External services | Wallet Storage (WALLET_STORAGE_URL), ARC (optional for payment transactions) |
| External services | Wallet Storage (WALLET_STORAGE_URL) |
| ts-stack packages | @bsv/sdk, @bsv/auth-express-middleware, @bsv/payment-express-middleware, @bsv/wallet-toolbox-client |

## HTTP endpoints
Expand All @@ -61,7 +61,7 @@ None.
| -------------------------- | -------- | --------------------------------------------------------------------------------------------- |
| PRICE_PER_GB_MO | No | Monthly storage price per GB (e.g., `0.03`) |
| HOSTING_DOMAIN | No | Public domain for server advertisement (e.g., `localhost:8080` or `https://uhrp.example.com`) |
| BSV_NETWORK | No | Target blockchain network (e.g., `mainnet` or `testnet`) |
| BSV_NETWORK | No | `mainnet`, `testnet`, `ttn`, or `teratestnet` (default `mainnet`) |
| WALLET_STORAGE_URL | No | Wallet storage endpoint for key derivation (e.g., `https://store-us-1.bsvb.tech`) |
| SERVER_PRIVATE_KEY | Yes | 256-bit hex private key for server identity |
| HTTP_PORT | No | Express server port (default: 8080) |
Expand Down Expand Up @@ -105,25 +105,32 @@ Files stored in `./public` or configured data directory.
# Build and start
npm run build && npm start

# Or as Docker container (lightweight ts-node, no Dockerfile provided)
# Or build the checked-in multi-stage production image
docker build -t uhrp-lite:local .
docker run -d \
-e SERVER_PRIVATE_KEY=<256-bit-hex> \
-e HOSTING_DOMAIN=https://uhrp.example.com \
-e HTTP_PORT=8080 \
-v uhrp_data:/app/public \
-p 8080:8080 \
node-uhrp-server:latest
uhrp-lite:local
```

No docker-compose.yml or nginx.conf provided; filesystem-based, no external database. Direct Express server on configured port.
The Dockerfile compiles TypeScript in a disposable Node 24 build stage, runs
the built server as the unprivileged `node` user, and probes `/ready`.
No compose file or external database is required.

## Migrations

None; stateless server with files stored directly on disk with JSON metadata.

## Health checks

Implicit health via GET / returning HTTP 200. No explicit health endpoint. Monitor disk space and file directory accessibility.
- `GET /health` and `GET /healthz` report process liveness.
- `GET /ready` returns 200 only after startup completes and 503 while the
process is starting or shutting down.
- The container health check probes `/ready`. Operators must additionally
monitor disk space and object-directory accessibility.

## Spec conformance

Expand Down
55 changes: 30 additions & 25 deletions docs/infrastructure/uhrp-server-cloud-bucket.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
id: infra-uhrp-cloud
title: 'UHRP Server (Cloud Bucket)'
kind: infra
version: '0.2.10'
last_updated: '2026-07-25'
last_verified: '2026-07-25'
version: '0.2.34'
last_updated: '2026-08-24'
last_verified: '2026-08-24'
review_cadence_days: 30
status: stable
tags: [uhrp, storage, cloud, google-cloud-run, production]
---

# UHRP Server (Cloud Bucket)

> A production-grade UHRP host server backed by Google Cloud Storage (or S3-compatible buckets). Stores large files in cloud buckets with optional billing/micropayments and includes advertising infrastructure for overlay network discovery.
> A production-grade UHRP host server backed by Google Cloud Storage. Stores large files in a cloud bucket with billing/micropayment support and notifier-driven advertising for overlay network discovery.

## What it does

Expand All @@ -21,7 +21,8 @@ workflows backed by Google Cloud Storage. Static object retrieval is public;
upload, list, find, and renewal require BRC-103 identity. A separate
administrative advertisement endpoint uses a strong Bearer token.

Clients upload files with authentication, retrieve files via public GET, and server continuously advertises hosting capability.
Clients request authenticated uploads, retrieve files via public GET, and use
the bucket notifier to trigger authenticated hosting advertisements.

## When to deploy this

Expand All @@ -35,8 +36,8 @@ Clients upload files with authentication, retrieve files via public GET, and ser

| Type | Requirement |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Database | Optional MySQL via Knex (for backup storage or metadata tracking); not required if using cloud-only |
| External services | Google Cloud Storage bucket, ARC API key, Wallet Storage, Bugsnag (optional) |
| Database | None; Google Cloud Storage is the object and metadata store |
| External services | Google Cloud Storage bucket and Wallet Storage |
| ts-stack packages | @bsv/sdk, @bsv/auth-express-middleware, @bsv/payment-express-middleware, @bsv/wallet-toolbox, @bsv/wallet-toolbox-client |

## HTTP endpoints
Expand All @@ -63,23 +64,23 @@ None; HTTP-only with background advertising worker.
| NODE_ENV | No | `development`, `staging`, or `production` |
| SERVER_PRIVATE_KEY | Yes | 256-bit hex private key for server identity |
| HOSTING_DOMAIN | No | Public HTTPS domain for advertising (e.g., `https://uhrp-storage.example.com`) |
| BSV_NETWORK | No | Target blockchain network (`main`, `test`, or `regtest`) |
| BSV_NETWORK | No | `mainnet`, `testnet`, `ttn`, or `teratestnet` (default `mainnet`) |
| WALLET_STORAGE_URL | No | Wallet storage endpoint (e.g., `https://store-us-1.bsvb.tech`) |
| PRICE_PER_GB_MO | No | Monthly storage price per GB for billing |
| ENABLE_PAYMENT_MIDDLEWARE | No | Set to `'true'` to require payment for uploads |
| GOOGLE_CLOUD_PROJECT | No | GCP project ID (auto-detected from service account if available) |
| GOOGLE_CLOUD_BUCKET | Yes | Cloud Storage bucket name (e.g., `uhrp-storage-prod`) |
| GOOGLE_APPLICATION_CREDENTIALS | No | Path to service account JSON key (for local/Cloud Run auth) |
| ARC_API_KEY | No | ARC API key for transaction broadcasting (advertising) |
| ADVERTISE_INTERVAL_MS | No | Interval for re-advertising to overlay (default: 3600000ms = 1 hour) |
| BUGSNAG_API_KEY | No | Bugsnag error reporting API key (optional) |
| MIN_HOSTING_MINUTES | No | Minimum requested retention period (default 180 minutes) |
| GCP_PROJECT_ID | Yes* | GCP project used for production signed upload URLs |
| GCP_BUCKET_NAME | Yes | Cloud Storage bucket name (e.g., `uhrp-storage-prod`) |
| GCP_STORAGE_CREDS | Yes* | JSON credentials used for production signed upload URLs; provide through a secret |
| ADMIN_TOKEN | Yes | At least 32 random characters for `/advertise` Bearer auth |
| UHRP_CORS_MODE | No | `public` (default), `allowlist`, or `disabled` |
| UHRP_CORS_ALLOWED_ORIGINS | No | Exact comma-separated origins in allowlist mode |
| UHRP_CORS_ALLOWED_HEADERS | No | Strict comma-separated browser request-header allowlist; omit for additive compatibility |
| UHRP_JSON_MAX_BODY_BYTES | No | JSON body ceiling (default 262144) |
| TRUST_PROXY_HOPS | No | Exact trusted proxy hop count, 0 through 10 |

`GCP_PROJECT_ID` and `GCP_STORAGE_CREDS` are required by the production
signed-upload path; the development path returns a local placeholder URL.

See [Public Service Edge Security](service-edge-security.md#uhrp-cloud-bucket-server)
for full edge controls.

Expand Down Expand Up @@ -112,7 +113,7 @@ gcloud run deploy uhrp-storage \
--image uhrp-storage:latest \
--platform managed \
--region us-central1 \
--set-env-vars SERVER_PRIVATE_KEY=<hex-key>,GOOGLE_CLOUD_BUCKET=uhrp-storage-prod,ENABLE_PAYMENT_MIDDLEWARE=true
--set-env-vars SERVER_PRIVATE_KEY=<hex-key>,GCP_PROJECT_ID=<project>,GCP_BUCKET_NAME=uhrp-storage-prod,ADMIN_TOKEN=<32+-character-token>

# Or deploy with docker-compose (local testing only)
docker compose up -d
Expand All @@ -122,34 +123,38 @@ Follows GCP 12-factor patterns: stateless design, cloud bucket for file storage,

## Migrations

Stateless; cloud bucket is source of truth. Optional MySQL Knex migrations for metadata tables if ENABLE_METADATA_DB=true.
None. The cloud bucket and object metadata are the storage authority.

## Health checks

Implicit health via /info endpoint (HTTP 200). Cloud Run readiness probe typically checks GET /info or GET /{hash} availability. No explicit /healthz endpoint.
- `GET /health` and `GET /healthz` report process liveness.
- `GET /ready` returns 200 after initialization and 503 while the process is
starting or shutting down.
- The checked-in container health check probes `/ready`.

## Spec conformance

- **UHRP** – Implements UHRP host protocol for file storage, retrieval, and metadata
- **BRC-103** – Mutual authentication on PUT, optional on GET/POST
- **BRC-100** – Payment verification for uploads (optional)
- **BRC-103** – Mutual authentication on upload, list, find, and renewal workflows
- **BRC-100** – Wallet-backed pricing and payment verification for upload and renewal
- **Google Cloud** – Follows Cloud Run best practices (health checks, graceful shutdown, 12-factor)

## Integration with ts-stack

- UHRP clients upload/retrieve files using SERVER_PRIVATE_KEY and HOSTING_DOMAIN
- Wallet Storage derives keys, validates payments, manages user accounts
- Background worker advertises UHRP host via SHIP overlay protocol using ARC broadcaster
- Optional Cloud SQL metadata database for query optimization
- Bugsnag integration for production error tracking and monitoring
- The bucket notifier calls the token-protected `/advertise` route, which
publishes the UHRP advertisement through the SDK SHIP broadcaster

## Common pitfalls

- GCP credentials: GOOGLE_APPLICATION_CREDENTIALS must point to valid service account JSON; Cloud Run uses default service account if not set
- Storage bucket policy: Ensure bucket exists and service account has storage.objects.create/get/delete permissions
- Cost management: Monitor storage usage and pricing; use Cloud Storage lifecycle policies for archival
- Payment enforcement: ENABLE_PAYMENT_MIDDLEWARE requires ARC_API_KEY and WALLET_STORAGE_URL; uploads fail if not configured
- Advertising loop: ADVERTISE_INTERVAL_MS should balance frequent updates vs transaction costs; 1 hour is conservative default
- Signed uploads: `GCP_PROJECT_ID`, `GCP_BUCKET_NAME`, and valid JSON in
`GCP_STORAGE_CREDS` must agree; malformed credentials fail URL creation
- Advertising: `ADMIN_TOKEN` must match the bucket notifier and contain at
least 32 characters
- Cloud Run and application request timeouts default to 60 seconds; use direct cloud upload workflows for large objects rather than unbounded application buffering
- Graceful shutdown: Cloud Run sends SIGTERM; ensure all writes complete before exit (transaction broadcasts, metadata flushes)

Expand Down
104 changes: 104 additions & 0 deletions docs/packages/wallet/ecpm-permission-module.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
id: ecpm-permission-module
title: '@bsv/ecpm-permission-module'
kind: package
domain: wallet
npm: '@bsv/ecpm-permission-module'
version: '0.1.0'
last_updated: '2026-08-24'
last_verified: '2026-08-24'
review_cadence_days: 30
status: experimental
tags: ['permissions', 'brc98', 'ecpm', 'cryptography']
repo: 'https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/ecpm-permission-module'
---

# @bsv/ecpm-permission-module

`@bsv/ecpm-permission-module` is the reference `p ecpm` semantic module for
BRC-100 wallet hosts. It applies or removes a wallet-derived scalar from an
arbitrary validated secp256k1 point while retaining the standard
`getPublicKey` request and response shapes.

Use this package when a protocol needs commutative point masking or another
point operation whose base is supplied by the application. Pure BRC-43 can
name a counterparty while deriving the scalar, but ordinary `getPublicKey`
still returns that scalar times the fixed generator; it cannot select the
caller's point as the multiplication base.

## Install

```bash
npm install @bsv/ecpm-permission-module @bsv/wallet-toolbox-client @bsv/sdk
```

## Protocol

Call the existing `getPublicKey` method with this protocol name inside the
normal `[securityLevel, protocolName]` tuple:

```text
p ecpm <apply|remove> <pointHex> <logicalProtocolID>
```

The module reads `keyID`, `counterparty`, `privileged`, `privilegedReason`, and
`seekPermission` from their existing fields. It derives the scalar under
`p ecpm <logicalProtocolID>`; the operation and point are deliberately omitted
so `remove` uses the inverse of the exact scalar selected by `apply`.

```typescript
const masked = await wallet.getPublicKey({
protocolID: [2, `p ecpm apply ${pointHex} mental poker deal`],
keyID: 'deck mask',
counterparty: 'self'
})

const restored = await wallet.getPublicKey({
protocolID: [2, `p ecpm remove ${masked.publicKey} mental poker deal`],
keyID: 'deck mask',
counterparty: 'self'
})
```

## Wallet installation

```typescript
import { createEcpmModule } from '@bsv/ecpm-permission-module'
import { WalletPermissionsManager } from '@bsv/wallet-toolbox-client'

const ecpm = createEcpmModule({
keyDeriver: setup.keyDeriver,
authorize: request => showTrustedWalletPrompt(request),
privilegedKeyDeriver: reason => acquirePrivilegedKeyDeriver(reason)
})

const wallet = new WalletPermissionsManager(setup.wallet, adminOriginator, {
permissionModules: { ecpm }
})
```

The privileged provider is optional. If an application requests
`privileged: true`, the module authorizes the supplied reason before asking the
host for a privileged deriver and fails closed when no provider is available.

## Security and permissions

- Only `getPublicKey` is accepted under `p ecpm`; signing, HMAC, and encryption
calls cannot reuse the ECPM-derived scalar.
- Identity-key and `forSelf` modes are rejected.
- Input points and public-key counterparties must be canonical lowercase,
compressed, finite secp256k1 points.
- Security level 0 ordinary calls do not prompt. Levels 1 and 2 require the
authorization callback; level 2 grants are scoped to the counterparty.
- Every privileged call requires authorization, and `seekPermission: false`
fails unless an applicable grant is already cached.
- The application receives only `{ publicKey }`, never the derived scalar or
either key-derivation provider.

## Module interface

Wallet Toolbox exposes the optional
`PermissionsModule.handleRequest(request, next)` semantic hook. A module can
return the standard BRC-100 result directly, as ECPM does, or call `next` at
most once. Existing `onRequest`/`onResponse` transformation modules remain
compatible.
Loading
Loading