Finished updating the readme

This commit is contained in:
2025-08-22 12:40:09 -04:00
parent 720c7e0b52
commit 0ff358552c

236
README.md
View File

@@ -1,24 +1,224 @@
# GreenCoast — Privacy-First, Shardable Social (Dockerized)
# GreenCoast
**Goal:** A BlueSky-like experience with **shards**, **zero-trust**, **no data collection**, **E2EE**, and easy self-hosting — from x86_64 down to **Raspberry Pi Zero**.
License: **The Unlicense** (public-domain equivalent).
This repo contains a minimal, working **shard**: an append-only object API with zero-data-collection defaults. Its structured to evolve into full federation, E2EE, and client apps, while keeping Pi Zero as a supported host.
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.
---
## Quick Start (Laptop / Dev)
## Features
**Requirements:** Docker + Compose v2
- **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.
```bash
git clone <your repo> greencoast
cd greencoast
cp .env.example .env
docker compose -f docker-compose.dev.yml up --build
# Health:
curl -s http://localhost:8080/healthz
# Put an object (dev mode allows unauthenticated PUT/GET):
curl -s -X PUT --data-binary @README.md http://localhost:8080/v1/object
# -> {"ok":true,"hash":"<sha256>",...}
curl -s http://localhost:8080/v1/object/<sha256> | head