⚒️

Hephaestus — the forge

A code host you sign into with a Bitcoin Cash wallet. Repositories on your own VPS, cold blobs on the Sia network. No email. No password reset. No middleman.

What Hephaestus is

A friction-free GitHub alternative for people who want to hold their own identity. Under the hood it's Forgejo (a soft-fork of Gitea) with a wallet-authentication layer bolted on and cold storage moved from disk to Sia.

Wallet is your identity

Generate or import a BCH wallet in your browser. Sign a one-time challenge to prove control. That's your account — nothing to remember except your recovery phrase.

Cold storage on Sia

Every LFS object, release archive, package registry blob and issue attachment lives on the Sia network via s3.silentmode.st:8600. Live git repos + Postgres stay on the VPS SSD for speed.

Standard git — push over HTTPS or SSH

Nothing weird. git clone, git push, PRs, issues, releases, container registry, the works. Bring your existing muscle memory.

Sign in — 30 seconds

  1. Open the forge. Go to code.silentmode.st and click Sign in, then Sign in with hephaestus-wallet.
  2. Generate or import a wallet. Pick "Generate new" for a fresh BIP-39 12-word recovery phrase, or "Import existing" if you already have one. Write it down — lose it and the account is gone.
  3. Set a browser passphrase (min 8 chars). Used to encrypt the wallet in your browser's localStorage. Never leaves your device.
  4. Click "Create wallet & sign in". The browser signs a challenge, the server verifies, Forgejo auto-provisions you as bch_<first-20-of-your-cashaddr>. You land in the dashboard.

Why no email? Nothing to leak, nothing to phish, no password-reset flow to hijack. The trade: if you lose your recovery phrase there is no support form to send you back in. Back the phrase up. Twice. On paper.

For AI sessions & programmatic testing read before debugging

If you're an AI assistant, CI job, or anyone debugging the sign-in without a real browser, read this first. It heads off two misdiagnoses we've already hit.

Misdiagnosis #1: "The login shell page is only 510 bytes — it's a broken stub"

It's not a stub — it's a deliberately small bootstrap page that renders the wallet UI client-side via a JS module (standard SPA pattern). Size ≠ brokenness. The full UI (mnemonic generation, passphrase encryption, sign flow) is inside wallet.js, not the shell. Test by requesting wallet.js instead:

curl -sI https://code.silentmode.st/auth/static/wallet.js
# expect: HTTP 200, ~10 KB, Content-Type: application/javascript

Misdiagnosis #2: "static/wallet.js returns 404 at every path"

It only 404s if you drop the /auth/ prefix. Caddy path-multiplexes: /auth/* goes to the auth-proxy, everything else goes to Forgejo. Forgejo has no /static/wallet.js, so that path is expected-404. The real paths are:

URLServed byPurpose
/auth/authorizeauth-proxyLogin shell HTML (SPA bootstrap)
/auth/static/wallet.jsauth-proxyWallet UI + signing logic (~10 KB)
/auth/static/login.cssauth-proxyStyles for the login card
/auth/challengeauth-proxyPOST cashaddr → get nonce + signed message
/auth/verifyauth-proxyPOST nonce + signature → get OAuth code
/auth/.well-known/openid-configurationauth-proxyOIDC discovery
/user/oauth2/hephaestus-walletForgejoKicks off the OAuth handshake (307 → /auth/authorize)
/user/oauth2/hephaestus-wallet/callbackForgejoReceives the OAuth code, mints session
/api/v1/versionForgejoPublic: returns Forgejo version
/api/v1/userForgejoRequires an API token (not a session cookie) — returning 401 in a browser is not a bug

Testing the sign-in flow end-to-end without a real browser

The wallet signing is easiest to reproduce in Node/Deno or a headless browser — the crypto is BIP-137 "Bitcoin Signed Message" over BCH secp256k1. But the server-side pieces alone can be smoke-tested with curl:

ISSUER=https://code.silentmode.st/auth

# 1. discovery document lists all endpoints
curl -s "$ISSUER/.well-known/openid-configuration" | jq .

# 2. request a challenge for any cashaddr (server just holds it 5 min)
curl -s -X POST -H 'content-type: application/json' \
  -d '{"cashaddr":"bitcoincash:qq00000000000000000000000000000000000000q"}' \
  "$ISSUER/challenge"
# → returns {"nonce": "...", "message": "code.silentmode.st wants you to sign in..."}

# 3. any BCH wallet that speaks "Sign Message" can produce a valid signature
#    for that exact message. Then POST it:
curl -s -X POST -H 'content-type: application/json' \
  -d '{"nonce":"THE_NONCE","signature":"BASE64_SIG"}' \
  "$ISSUER/verify"
# → returns {"redirect": "https://code.silentmode.st/user/oauth2/hephaestus-wallet/callback?code=..."}

For the JS side (which is what the browser does), inspect auth-proxy/public/wallet.js in the repo — it's ~250 lines and does exactly this: generateBip39Mnemonic → deriveHdPath("m/44'/145'/0'/0/0") → sign(magicHash(message)). Nothing exotic.

Common browser-side checks

If you're in a browser (or a browser-driving tool) and want to know if you're actually signed in:

// The URL bar isn't reliable when Forgejo shows Security or Settings pages —
// they render for logged-in users but don't have "Dashboard" in the title.
// Best check: does the profile menu render, and is there no "Sign in" link?

const signedIn = !document.querySelector('a[href*="/user/login"]')
              && !!document.querySelector('[aria-label*="Profile"], .user-menu, .avatar');

// The username, if you're on the Dashboard:
document.title.match(/^(bch_\w+)/)?.[1];

Do not test with fetch('/api/v1/user') unless you have an API token. That endpoint requires token auth even when you're signed into the web UI. Session cookies get you into Forgejo pages; they don't get you into the token-scoped API. A 401 there means "no token" — it does not mean "not signed in."

Push some code

Two paths, either works. Pick whichever fits your setup.

Over HTTPS (with an access token)

Log in, open Settings → Applications → Generate New Token, give it write:repository scope. Then:

git clone https://your_bch_username:YOUR_TOKEN@code.silentmode.st/your_bch_username/your-repo.git
cd your-repo
echo "Hello, forge" > README.md
git add README.md && git commit -m "hello"
git push

Over SSH (with your existing key)

Add your public key to Settings → SSH / GPG Keys → Add Key. Then use port 2222:

git clone ssh://git@code.silentmode.st:2222/your_bch_username/your-repo.git

Your key is only trusted for git-over-SSH; there's no shell access.

How it fits together

            you (browser)
                │
       https://code.silentmode.st/
                │
     ┌──────────┴──────────┐
     │        Caddy        │  ← Let's Encrypt cert, HTTP/3
     │   (path-based mux)  │
     └──┬───────────────┬──┘
        │               │
        │/auth/*        │/
        ▼               ▼
   auth-proxy       Forgejo ── Postgres      (live git repos + DB
   (OIDC provider) ───┬───                    on local VPS SSD)
   Node/Fastify       │
   libauth for        │ storage backend
   BCH signature      ▼
   verification    s3.silentmode.st:8600  ← s3d gateway
                       │
                       ▼
                  Sia network hosts
                  (LFS, attachments,
                   packages, archives)
ComponentRole
Forgejo 10Web UI, PRs, issues, releases, container/npm registry, git-over-HTTPS + SSH
Postgres 16User records, issues, PRs, permissions, session state
Caddy 2Reverse proxy + automatic Let's Encrypt TLS + HTTP/3
auth-proxyOIDC provider (Node/Fastify + libauth). Issues signed challenges, verifies BCH signatures, mints OAuth codes.
s3d → SiaCold storage. Files are encrypted client-side, sharded via Reed-Solomon, scattered across independent hosts.
resticNightly encrypted snapshots (Postgres + /data/git) to a separate Sia bucket. Systemd timer at 03:15 UTC.

Wallet-auth protocol

Any BCH wallet that speaks Bitcoin Signed Message can produce a signature Hephaestus will accept. There is no Hephaestus-specific signing format.

Message the wallet signs:
─────────────────────────────
code.silentmode.st wants you to sign in with your Bitcoin Cash account:
bitcoincash:qzkc695pm4r3p7kcq36sh0t3fdu5e6ua7u2ar36vea

By signing, you prove you control this address. This request will not
trigger a blockchain transaction or cost any fees.

Domain: code.silentmode.st
Nonce: e65d80b51d3f1baa2efb8d952d827104
Issued At: 2026-08-29T17:50:12.655Z

Signature is standard BIP-137-style recoverable ECDSA over the SHA256(SHA256(varint(magic) || magic || varint(msg) || msg)) digest, base64-encoded. The server recovers the pubkey, derives the cashaddr, and matches it against the address you claimed. Nonces expire after 5 minutes and are single-use.

OIDC id_token claims: sub = full cashaddr, preferred_username = "bch_" + first-20-of-address, plus a custom cashaddr claim carrying the full address for downstream tools. Signed with EdDSA (Ed25519).

For operators self-host your own

Hephaestus is a docker-compose stack. Everything you need is in the silentmode monorepo under Hephaestus/. To stand up your own instance:

  1. Get a Linux VPS with Docker (2 GB RAM, ~10 GB free disk minimum). A domain name pointing at it. Ports 80/443/2222 open.
  2. Clone the repo, copy the env template
    git clone ssh://git@code.silentmode.st:2222/silentmode/silentmode.git
    cd silentmode/Hephaestus
    cp .env.example .env
    # fill in real values — see .env.example for what each field means
  3. Bring it up
    docker compose up -d
    First run pulls ~500 MB of images and builds the auth-proxy. Give it a few minutes.
  4. Complete Forgejo install via POST to / with your admin creds (see PROTOCOL.md in the repo for the exact field list — Forgejo's --config path prevents the CLI installer working, so it has to be the HTTP path).
  5. Register the wallet OIDC provider
    docker compose exec forgejo forgejo \
        --config /data/gitea/conf/app.ini admin auth add-oauth \
        --provider openidConnect \
        --name hephaestus-wallet \
        --key "$AUTH_PROXY_CLIENT_ID" --secret "$AUTH_PROXY_CLIENT_SECRET" \
        --auto-discover-url "$AUTH_PROXY_ISSUER/.well-known/openid-configuration" \
        --scopes "openid profile"

Gotchas we hit — save yourself the same debugging

Status

Live atcode.silentmode.st
SignupOpen (wallet-only; local registration disabled)
StorageSia network for cold blobs, VPS SSD for hot path
BackupsNightly restic → separate Sia bucket, encrypted client-side
Sourcesilentmode/silentmode (private — request access from an admin), or the local checkout under Hephaestus/
LicenseForgejo is GPL-3.0. Auth-proxy + Caddyfile + docker-compose scaffolding is part of the Silent Mode monorepo.
ChainChipnet (Bitcoin Cash test network) alpha
BCNR namehephaestus.x — minted on chipnet, points at https://code.silentmode.st/. Resolve via navigate.st gateway or any BCNR-aware browser (Theseus, Ariadne).

Report an issue. Open one at silentmode/silentmode/issues once you have access, or ping in the usual Silent Mode channels.