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.
Dani 1f2d2cf30b Added the examples to .env.example
Fixed Anon Plaintext posting.
Need to fix the DeviceSignin
Changed the layout to be more Facebook-esqe
2025-08-22 22:56:15 -04:00
2025-08-22 12:00:58 -04:00
2025-08-21 20:56:38 -04:00
2025-08-21 20:56:38 -04:00
2025-08-21 20:56:38 -04:00
2025-08-22 12:42:22 -04:00
2025-08-21 20:57:23 -04:00
2025-08-22 18:24:24 -04:00
2025-08-22 18:24:24 -04:00
2025-08-22 12:00:58 -04:00
2025-08-21 20:56:38 -04:00
2025-08-21 20:56:38 -04:00
2025-08-21 20:56:38 -04:00
2025-08-22 12:40:09 -04:00

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.

Description
No description provided
Readme 217 KiB
Languages
Go 58.6%
JavaScript 21.4%
HTML 7.8%
HCL 5.5%
CSS 3.3%
Other 3.4%