# Magistry online upload broker

This worker keeps the Magistry Pinata JWT server-side. It can act as the online fallback for Lit/Chipotle encrypted-upload outages, and it can issue short-lived signed Pinata upload URLs for unencrypted public artifacts such as village proposal bundles.

## Endpoints

- `GET /health`
- `POST /v1/upload`
- `POST /v1/registry-head`
- `POST /v1/administrative-artifact`
- `POST /v1/public-artifact-signed-upload`
- `GET /v1/registry-inbox?limit=100`

## Required secrets and environment

Set these in the host platform, not in browser code:

- `PINATA_JWT`: Pinata V3 API key with Files: Write and Groups: Write when using a group.
- `MAGISTRY_PINATA_GROUP_ID`: public Pinata group used for incoming registry records.

Recommended:

- `MAGISTRY_ALLOWED_ORIGINS`: comma-separated allowed site origins. If omitted, CORS uses `*`.
- `ETHEREUM_RPC_URL`: Ethereum mainnet JSON-RPC endpoint for fee receipt checks. Defaults to `https://ethereum-rpc.publicnode.com`.
- `MAGISTRY_PAYMENT_WALLET`: fee recipient wallet. Defaults to the Chief Magistrate wallet in source.
- `MAGISTRY_REGISTRY_HEAD_SIGNERS`: comma-separated wallets allowed to publish signed registry heads. Defaults to the Chief Magistrate wallet in source.
- `MAGISTRY_APPROVED_UPLOAD_WALLETS`: comma-separated wallet allowlist.
- `MAGISTRY_RESTRICTED_UPLOAD_DOMAINS`: comma-separated domains that require the allowlist. Leave empty to require the allowlist for every upload when `MAGISTRY_APPROVED_UPLOAD_WALLETS` is set.
- `MAGISTRY_ADMINISTRATIVE_ARTIFACT_PRICE_KRNS`: KRNS price for `/v1/administrative-artifact`; defaults to `0.25`.
- `MAGISTRY_PUBLIC_ARTIFACT_PRICES_KRNS`: optional JSON map of purpose-specific KRNS prices, such as `{"village-proposal-preview-publication":"0.1","village-proposal-full-publication":"5"}`.
- `MAGISTRY_FREE_ADMINISTRATIVE_ARTIFACT_WALLETS`: comma-separated wallets that may use `/v1/administrative-artifact` without a KRNS payment. The Chief Magistrate wallet is always free.
- `MAGISTRY_ADMINISTRATIVE_ARTIFACT_PINATA_GROUP_ID`: optional Pinata group for administrative/public artifacts. Falls back to `MAGISTRY_PINATA_GROUP_ID`.
- `MAGISTRY_ADMINISTRATIVE_ARTIFACT_MAX_BYTES`: optional byte limit for brokered administrative/public artifacts. Defaults to 10 MB.
- `MAGISTRY_PUBLIC_ARTIFACT_MAX_BYTES`: optional byte limit for signed public artifact upload URLs. Defaults to 20 MB.
- `MAGISTRY_PUBLIC_ARTIFACT_SIGNED_UPLOAD_EXPIRES_SECONDS`: signed upload URL lifetime. Defaults to 600 seconds and is capped between 60 and 3600 seconds.
- `MAGISTRY_PINATA_SIGNED_UPLOAD_ENDPOINT`: optional Pinata signed-upload API endpoint override. Defaults to `https://api.pinata.cloud/v3/files/sign`.

Emergency-only:

- `MAGISTRY_SKIP_PAYMENT_VERIFICATION=true`: bypasses Ethereum receipt checks. Use only for isolated testing.

## Browser config

Expose only the public broker URL in the Magistry site config:

```js
// globalThis.MAGISTRY_UPLOAD_BROKER_ENDPOINT = "https://upload.magistry.eth";
// globalThis.MAGISTRY_UPLOAD_BROKER_ADMINISTRATIVE_ARTIFACT_ENDPOINT = "https://upload.magistry.eth/v1/administrative-artifact";
// globalThis.MAGISTRY_PUBLIC_ARTIFACT_SIGNED_UPLOAD_ENDPOINT = "https://upload.magistry.eth/v1/public-artifact-signed-upload";
```

The browser will try Lit first when Lit is configured. If Lit fails with a network/cache/funds failure, it falls back to this broker. Uploads, signed registry-head publication, and the Registry inbox all follow that pattern. `POST /v1/registry-head` exists as the fallback path for signed registry heads, while the primary path is the Lit registry-head publisher action.

`POST /v1/administrative-artifact` is the universal public artifact fallback for branch documents, proposal generated-site manifests, proposal registry JSON, and future public artifacts. It never decides legal authority; it only verifies file integrity, optional KRNS payment, and optional free-wallet eligibility before pinning to IPFS.

`POST /v1/public-artifact-signed-upload` is preferred for unencrypted public proposal artifacts. It verifies the wallet/payment policy and artifact metadata, returns a short-lived Pinata upload URL, and lets the browser upload the bytes directly to Pinata. This avoids pushing large public files through Lit or through the worker body.
