State-channel HTML hosting
You spin up a state machine and you can host a webapp on it. The pitch is spin-up-as-webapp — no separate Vercel, no separate Netlify, no separate CDN. Your zsm_ key already authorizes uploads; the machine is already on the entangled state; the bytes you upload are hash-committed to your audit log on every change.
The framework runs the server. The framework enforces a strict CSP at serve time. The framework forces MIME from extension. The framework path-normalizes against your machine root. The framework writes an entangled state row on every serve. You get a fully-audited, sandboxed mini-webapp, billed against your tally.
What it is
- A per-slug HTML hosting tier served at
https://zeqsdk.com/s/:slug/*. - Bytes uploaded via authenticated POST to
/api/chain/:slug/site/upload. - Bytes served under a sandbox CSP with isolated cookies — no
zeq.devcredentials in scope. - Strict CSP with isolated cookies.
- Hash commitment on the entangled state on every byte stream served.
- Free tier: hash-only — the chain commits the hash of each upload and the serve path 302s to an
external_url(no bytes stored). Paid tier: the bytes themselves are stored in a machine-isolated bucket and served directly. Throughput is rate-limited per slug.
What it is not
- Not a CDN. Throughput is rate-limited. For high-volume static, use a CDN and beacon to your machine from the CDN edge.
- Not a runtime. There's no server-side execution; this is static hosting + framework-controlled response headers.
- Not a database. State should live in your audit entangled state, your contracts, your tally — not in the static bytes.
Routes
| Method | Path | Auth | Notes |
|---|---|---|---|
POST | /api/chain/:slug/site/upload | Bearer (operator+) | Upload a byte stream (multipart or base64). Hash committed on the entangled state. |
GET | /api/chain/:slug/site/list | Bearer (viewer+) | List the pages for the slug with their hashes. |
GET | /api/chain/:slug/site/file/:sha | Bearer (viewer+) | Single-file metadata by content hash. |
DELETE | /api/chain/:slug/site/* | Bearer (admin+) | Soft-delete (archive). Records the change on the entangled state. |
GET | /s/:slug/* | none | Public serve. Sandbox CSP. MIME-forced. |
Each upload writes:
- A byte commitment to the audit entangled state (
state_hash: sha256(bytes)). - (Paid tier) the bytes themselves to the machine's bucket.
Each public serve writes:
- An audit row (
state_hash: sha256(bytes)). - The egress counter against your tally.
CSP at serve time
Every served byte stream gets the same response headers, framework-set, NOT user-overridable:
Content-Type: <forced from extension>
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline'; img-src 'self' data:;
font-src 'self' data:; connect-src 'self';
frame-ancestors 'none'; form-action 'self';
base-uri 'none';
sandbox allow-scripts allow-forms allow-same-origin
allow-popups allow-popups-to-escape-sandbox;
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer
X-Zsm-Content-Sha256: <hash of bytes>
The CSP allows 'unsafe-inline' for scripts and styles (so embedded apps work) but locks connect-src to 'self'. A hosted page reaches the kernel through the same-origin Site SDK proxy at /s/:slug/sdk/zeq/:op.
X-Zsm-Content-Sha256 lets external verifiers hash the bytes themselves and compare to the entangled state commitment — proof that the framework served what the entangled state claims.
The hash-commitment-only doctrine
Free-tier hosting commits the hash of every uploaded byte stream to the entangled state, and the entangled state remains the ground truth.
This is consistent with the framework's audit-log doctrine: bytes are off-chain; hashes are on-chain; the entangled state is the witness.
For paid-tier hosting, bytes are persisted in a machine-isolated bucket and the manifest tracks (path, hash, bucket_url, bytes). The entangled state row also points to the bucket, so a future verifier can fetch and re-hash without trusting the framework's serve.
Use cases this is built for
- Marketing landing for a machine. Spin up
my-launch, uploadindex.html+style.css, sharehttps://zeqsdk.com/s/my-launch/. Every visitor's view is on your entangled state. - Client-side dashboard for an entangled state. Upload an HTML/JS dashboard that talks to
/api/chain/:slug/explore/ssevia the same-origin gate. Public dashboards foris_publicmachines work without auth. - Companion docs for a contract. Upload a static page describing your deployed contract; link from the contract definition's
descriptionfield. - Static API stub. Upload a JSON file at
/manifest.json, have a partner read it viaGET /s/:slug/manifest.json. Hash on the entangled state proves what they read.
Use cases this is NOT built for
- Real-time multiplayer apps (no server-side runtime).
- High-volume CDN-style static (use a real CDN + beacon to your machine).
- Anything that wants to read
zeq.devcookies (you can't — separate origin). - Anything that wants to call random external APIs (you can't — strict
connect-src).
Next
- State machines — the binding.