This repository has been archived on 2025-08-23. You can view files and clone it, but cannot push or open issues or pull requests.
Files
GreenCoast/README.md
2025-08-22 12:40:09 -04:00

225 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GreenCoast
A privacy-first, shardable social backend + minimalist client. **Zero PII**, **zero passwords**, optional **E2EE per post**, and **public-key accounts**. Includes **DPoP-style proof-of-possession**, **Discord SSO with PKCE**, and a tiny static client.
---
## Features
- **Zero-trust by design**: server stores no emails or passwords.
- **Accounts = public keys** (Ed25519 or P-256). No usernames required.
- **Proof-of-possession (PoP)** on every authenticated API call.
- **Short-lived tokens** (HMAC “gc2”) bound to device keys.
- **Shardable storage** (mTLS or signed shard requests).
- **No fingerprinting**: no IP/UA logs; coarse timestamps optional.
- **Static client** with strong CSP; optional E2EE per post.
- **Discord SSO (PKCE)** as an *optional* convenience.
- **Filesystem storage** supports both **flat** and **nested** object layouts.
---
## Architecture (brief)
- **Shard**: stateless API + local FS object store + in-memory index.
- **Client**: static files (HTML/JS/CSS) served by the shard or any static host.
- **Identity**: device key (P-256/Ed25519) or passkey; server mints short-lived **gc2** tokens bound to the device key (`cnf` claim).
- **Privacy**: objects can be plaintext (public) or client-encrypted (private).
---
## Security posture
- **Zero-trust**: no passwords/emails; optional SSO is *linking*, not source-of-truth.
- **DPoP-style PoP** on requests:
- Client sends:
- `Authorization: Bearer gc2.…`
- `X-GC-Key: p256:<base64-raw>` (or `ed25519:…`)
- `X-GC-TS: <unix seconds>`
- `X-GC-Proof: sig( METHOD "\n" URL "\n" TS "\n" SHA256(body) )`
- Server verifies `gc2` signature, key binding (`cnf`), timestamp window, and replay cache.
- **Replay protection**: 10-minute proof cache.
- **No fingerprinting/logging**: no IPs, no UAs.
- **Strict CSP** for client: blocks XSS/token theft.
- **Limits**: request body limits (default 10 MiB), simple per-account rate limiting.
- **Shard↔shard**: mTLS or per-shard signatures with timestamp + replay cache.
---
## Requirements
- Go 1.21+
- Docker (optional)
- A signing key for tokens: `GC_SIGNING_SECRET_HEX` (32+ bytes hex)
- (Optional) Discord OAuth app (Client ID/Secret + redirect URI)
- (Optional) Cloudflare Tunnel or other TLS reverse proxy
---
## Environment variables
GC_HTTP_ADDR=:9080
GC_HTTPS_ADDR= # optional
GC_TLS_CERT= # optional
GC_TLS_KEY= # optional
GC_STATIC_ADDR=:9082
GC_STATIC_DIR=/opt/greencoast/client
GC_DATA_DIR=/var/lib/greencoast
GC_ZERO_TRUST=true
GC_COARSE_TS=false
GC_SIGNING_SECRET_HEX=<64+ hex chars> # required for gc2 tokens
GC_REQUIRE_POP=true # default true; set false for first-run
# Dev convenience (testing only; disable for production)
GC_DEV_ALLOW_UNAUTH=false
GC_DEV_BEARER=
# Discord SSO (optional)
GC_DISCORD_CLIENT_ID=
GC_DISCORD_CLIENT_SECRET=
GC_DISCORD_REDIRECT_URI=https://greencoast.example.com/auth-callback.html
---
## Quickstart (Docker)
Minimal compose for local testing (PoP disabled + dev unauth allowed for first run):
services:
shard-test:
build: .
environment:
- GC_HTTP_ADDR=:9080
- GC_STATIC_ADDR=:9082
- GC_STATIC_DIR=/opt/greencoast/client
- GC_DATA_DIR=/var/lib/greencoast
- GC_ZERO_TRUST=true
- GC_SIGNING_SECRET_HEX=7f6e1a0f2b4d7e3a... # replace with your secret
- GC_REQUIRE_POP=false # easier first-run
- GC_DEV_ALLOW_UNAUTH=true
volumes:
- ./testdata:/var/lib/greencoast
- ./client:/opt/greencoast/client:ro
ports:
- "9080:9080"
- "9082:9082"
Open `http://localhost:9082` → set the Shard URL (`http://localhost:9080`) → publish a test post.
When ready, **turn PoP on** by removing `GC_REQUIRE_POP=false` and disabling `GC_DEV_ALLOW_UNAUTH`.
---
## Cloudflare Tunnel example
ingress:
- hostname: greencoast.example.com
service: http://shard-test:9082
- hostname: api-gc.greencoast.example.com
service: http://shard-test:9080
- service: http_status:404
Use “Full (strict)” TLS and ensure your cert covers both hosts.
---
## Client usage
- **Shard URL**: set it in the top “Connect” section (or use `?api=` query or `<meta name="gc-api-base">`).
- **Device key sign-in (no OAuth)**:
1) Client generates/stores a P-256 device key in the browser.
2) Client calls `/v1/auth/key/challenge` then `/v1/auth/key/verify` to obtain a **gc2** token bound to that key.
- **Discord SSO (optional)**:
- Requires `GC_DISCORD_CLIENT_*` env vars and a valid `GC_DISCORD_REDIRECT_URI`.
- Uses PKCE (`S256`) and binds the minted **gc2** token to the device key presented at `/start`.
---
## API (overview)
- `GET /healthz` liveness
- `PUT /v1/object` upload blob (headers: optional `X-GC-Private: 1`, `X-GC-TZ`)
- `GET /v1/object/{hash}` download blob
- `DELETE /v1/object/{hash}` delete blob
- `GET /v1/index` list indexed entries (latest first)
- `GET /v1/index/stream` SSE updates
- `POST /v1/admin/reindex` rebuild index from disk
- **Auth**
- `POST /v1/auth/key/challenge``{nonce, exp}`
- `POST /v1/auth/key/verify` `{nonce, alg, pub, sig}``{bearer, sub, exp}`
- `POST /v1/auth/discord/start` (requires `X-GC-3P-Assent: 1` and `X-GC-Key`)
- `GET /v1/auth/discord/callback` → redirects with `#bearer=…`
- **GDPR**
- `GET /v1/gdpr/policy` current data-handling posture
> When `GC_REQUIRE_POP=true`, all authenticated endpoints require PoP headers.
### PoP header format (pseudocode)
Authorization: Bearer gc2.<claims>.<sig>
X-GC-Key: p256:<base64-raw> # or ed25519:<base64-raw>
X-GC-TS: <unix seconds>
X-GC-Proof: base64(
Sign_device_key(
UPPER(METHOD) + "\n" + URL + "\n" + X-GC-TS + "\n" + SHA256(body)
)
)
---
## Storage layout & migration
- **Writes** are flat: `objects/<hash>`
- **Reads** (and reindex) also support:
- `objects/<hash>/blob|data|content`
- `objects/<hash>/<single file>`
- `objects/<prefix>/<hash>` (two-level prefix)
- To **restore** data into a fresh container:
1) Mount your objects at `/var/lib/greencoast/objects`
2) Call `POST /v1/admin/reindex` (with auth+PoP or enable dev unauth briefly)
---
## Reindex examples
Unauth (dev only):
curl -X POST https://api-gc.yourdomain/v1/admin/reindex
With bearer + PoP (placeholders):
curl -X POST https://api-gc.yourdomain/v1/admin/reindex ^
-H "Authorization: Bearer <gc2_token>" ^
-H "X-GC-Key: p256:<base64raw>" ^
-H "X-GC-TS: <unix>" ^
-H "X-GC-Proof: <base64sig>"
---
## Hardening checklist (prod)
- Set `GC_REQUIRE_POP=true`, remove dev bypass.
- Keep access token TTL ≤ 8h; rotate signing key periodically.
- Static client served with strong CSP (already enabled).
- Containers run non-root, read-only FS, `no-new-privileges`, `cap_drop: ["ALL"]`.
- Edge WAF/rate limits; 10 MiB default request cap (tunable).
- Commit `go.sum`; run `go mod verify` in CI.
---
## GDPR
- Server stores **no PII** (no emails, no IP/UA logs).
- Timestamps are UTC (or coarse UTC if enabled).
- `/v1/gdpr/policy` exposes current posture.
- Roadmap: `/v1/gdpr/export` and `/v1/gdpr/delete` to enumerate/remove blobs signed by a given key.
---
## License
This project is licensed under **The Unlicense**. See `LICENSE` for details.