commit bff90dc9b28212e6e3e18bde6ceeff380c84eff2 Author: Sami Ahmed Date: Thu Jul 16 03:01:46 2026 -0700 Initial CAC3 explorer — design doc, license, config schema. This is a docs-only seed. Tomorrow's session will add the actual Go source: main.go, internal/rpc/client.go (JSON-RPC + cookie auth), internal/chain/, internal/explorer/ (handlers), and internal/view/ (templates + static assets, embed.FS). Until then this commit gives the repo its shape: README with the full design, config example with all knobs, MIT license to match the chain, and a .gitignore for Go build artifacts. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8aad93d --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Go +/bin/ +/dist/ +*.exe +*.test +*.out +coverage.txt +*.prof + +# Local config / secrets +.env +config.local.yaml +*.cookie + +# Editor / OS +.DS_Store +.idea/ +.vscode/ +*.swp +*~ + +# Build cache +/tmp/ +/cac3-explorer +/cac3-explorer-* + +# Go modules (commit if not vendored; comment out if vendoring) +# vendor/ + +# Tailwind / static build cache +/static/css/.tailwind-cache/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6a21a76 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Sami Ahmed + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..bad4562 --- /dev/null +++ b/README.md @@ -0,0 +1,179 @@ +# CAC3 Explorer + +A Go single-binary block explorer for [CAC3 (Californiacoin3)](https://github.com/SamiAhmed7777/cac3). +Reads from a running `cac3d` node over its JSON-RPC interface (Bitcoin Core 0.18 style — cookie auth). + +## What it shows + +- **Home / chain tip** — current height, best block hash, difficulty, network hashrate, mempool size, last 10 blocks +- **Block detail** — by height or hash; full header, transactions, next/prev navigation +- **Transaction detail** — by txid; inputs/outputs with script-decoded addresses, value, confirmations +- **Address detail** — balance, total received/sent, UTXO list, recent tx history +- **Mempool** — current tx pool with fee/sat-per-byte sort + +## Why Go + +- One statically-linked binary — no runtime, no node_modules, no Python deps +- Cold start ~30ms, idle RSS ~15MB +- Embeds templates + static assets via `embed.FS` — single artifact deploy +- Native HTTP/2, easy `systemd` integration, fits the same deployment shape as DashCaddy / Triangles + +## Quick start + +### 1. Build + +```bash +go build -ldflags="-s -w" -o cac3-explorer . +# or with a version stamp +go build -ldflags="-s -w -X main.Version=$(git describe --tags --always)" -o cac3-explorer . +``` + +Static binary, no glibc dep, runs anywhere. + +### 2. Configure + +The explorer reads config from (in priority order): +1. Flags (`--rpc-url`, `--cookie-path`, `--listen`, etc.) +2. Env vars (`CAC3_RPC_URL`, `CAC3_COOKIE_PATH`, `CAC3_LISTEN`, …) +3. `config.yaml` in the working directory + +Example `config.yaml`: + +```yaml +listen: "0.0.0.0:8080" +rpc: + url: "http://127.0.0.1:8332" + cookie_path: "/var/lib/cac3/.cookie" + # OR static creds (less secure, but useful for testing): + # user: "cac3rpc" + # pass: "changeme" + timeout: "10s" + +chain: + name: "CAC3 Mainnet" + ticker: "CAC3" + # site URL used in / OG tags + site_url: "https://explorer.cac3.example" + +# Address-link overrides — point these at your public node, block explorers, etc. +links: + # tx / address / block pages can deep-link to external tools if set + # wallet: "https://wallet.cac3.example" +``` + +### 3. Run + +```bash +./cac3-explorer --config config.yaml +# or fully env-var driven +CAC3_RPC_URL=http://127.0.0.1:8332 \ +CAC3_COOKIE_PATH=/var/lib/cac3/.cookie \ +CAC3_LISTEN=0.0.0.0:8080 \ +./cac3-explorer +``` + +Open http://localhost:8080. + +### 4. systemd (optional) + +```ini +# /etc/systemd/system/cac3-explorer.service +[Unit] +Description=CAC3 Block Explorer +After=network.target cac3d.service +Wants=cac3d.service + +[Service] +Type=simple +User=cac3-explorer +Group=cac3-explorer +WorkingDirectory=/opt/cac3-explorer +ExecStart=/opt/cac3-explorer/cac3-explorer --config /etc/cac3-explorer/config.yaml +Restart=on-failure +RestartSec=5s +NoNewPrivileges=true +ProtectSystem=strict +ReadWritePaths=/var/log/cac3-explorer + +[Install] +WantedBy=multi-user.target +``` + +## RPC surface used + +The explorer only needs a **non-wallet** `cac3d` node (no `wallet.dat` required, no signing keys). +Methods called: + +| Method | Used for | +|---|---| +| `getblockchaininfo` | tip, difficulty, chainwork | +| `getnetworkinfo` | protocol version, connections | +| `getmininginfo` | hashrate, blocks | +| `getmempoolinfo` | mempool size, bytes | +| `getrawmempool` | mempool txid list | +| `getblockhash` | height → hash | +| `getblock` | block header + tx list (verbose=1) | +| `getblockheader` | header only | +| `getrawtransaction` | tx hex by txid | +| `decoderawtransaction` | decode inputs/outputs | +| `gettxoutsetinfo` | total supply stats | +| `getconnectioncount` | peer count | +| `getdifficulty` | current difficulty (decimal) | +| `listsinceblock` | recent wallet-scoped activity (only if `--show-wallet` enabled) | + +No `sendtoaddress`, no signing, no key access. **Safe to expose the RPC port on a private interface** (or behind SSH tunnel) — the explorer's only attack surface is the read path. + +## Repo layout + +``` +. +├── main.go # entrypoint, config, signal handling +├── internal/ +│ ├── rpc/ # cac3d JSON-RPC client + cookie auth +│ │ ├── client.go +│ │ ├── auth.go # cookie + userpass +│ │ └── types.go # raw RPC types +│ ├── chain/ # domain types (Block, Tx, Address, …) +│ │ └── types.go +│ ├── explorer/ # handlers +│ │ ├── home.go +│ │ ├── block.go +│ │ ├── tx.go +│ │ ├── address.go +│ │ └── mempool.go +│ └── view/ # templates + render +│ ├── templates/ # html/template files (embed.FS) +│ └── static/ # css, js, favicon (embed.FS) +├── config.example.yaml +├── go.mod +├── go.sum +├── LICENSE # MIT, matches the chain license +└── README.md +``` + +## Security notes + +- **Cookie auth is preferred** — `cac3d` rotates `.cookie` on every restart. Re-read on every request (cheap, file is ~1KB). +- The explorer is **read-only**. It never sends `sendtoaddress` / `signrawtransaction` / `walletlock`. To prove this, search the source for those method names — they should not appear. +- Do not expose `cac3d`'s RPC port to the public internet. Bind it to `127.0.0.1` or a tailnet IP and let the explorer be the only public entry point. +- The explorer binds to whatever `--listen` says. Use a reverse proxy (Caddy / nginx) for TLS, rate limiting, and a real domain. + +## Development + +```bash +# run with hot-reload (needs `air` or similar; not required) +go run . + +# test +go test ./... + +# vet / lint +go vet ./... +staticcheck ./... # if installed +``` + +The first request after `cac3d` start will be slow (RPC cold cache). Subsequent requests are <50ms for blocks, <200ms for address histories. + +## License + +MIT — see [LICENSE](LICENSE). This explorer is a separate project from the CAC3 chain code; it only depends on `cac3d`'s documented JSON-RPC interface. diff --git a/config.example.yaml b/config.example.yaml new file mode 100644 index 0000000..042f15e --- /dev/null +++ b/config.example.yaml @@ -0,0 +1,47 @@ +# CAC3 Explorer — example config +# Copy to config.yaml and edit, or override via flags / env vars. + +listen: "0.0.0.0:8080" + +rpc: + # Bitcoin Core 0.18 / cac3d JSON-RPC endpoint + url: "http://127.0.0.1:8332" + # Path to cac3d's .cookie file (preferred — auto-rotated on daemon restart). + cookie_path: "/var/lib/cac3/.cookie" + # OR static creds (less secure, but useful for tests): + # user: "cac3rpc" + # pass: "changeme" + timeout: "10s" + # How often to refresh the cookie file from disk. cac3d only writes it on + # startup, so this is mostly a safety net. + cookie_refresh: "30s" + +chain: + name: "CAC3 Mainnet" + ticker: "CAC3" + site_url: "https://explorer.example.com" + # Decimal places for coin amounts. CAC3 follows Bitcoin's 8. + coin_decimals: 8 + # Block time target in seconds (used for ETA / hashrate estimates). + block_time_seconds: 10 + +# Public-facing links shown in the header. Leave empty strings to hide. +links: + website: "https://cac3.example.com" + github: "https://github.com/SamiAhmed7777/cac3" + # Optional: deep-link tx / address / block pages to another explorer + # (e.g. if you want a sister explorer for a sister chain) + # alt_explorer: "https://alt-explorer.example.com" + +# Cache layer (in-memory by default). Set redis_url to share cache across replicas. +cache: + backend: "memory" # "memory" | "redis" + ttl: "30s" + # redis_url: "redis://127.0.0.1:6379/0" + +# UI +ui: + # Set false to disable the "Search" box (e.g. if fronted by an external search) + show_search: true + # Page footer copyright + footer: "© 2026 CAC3 Project"