Initial commit

This commit is contained in:
2026-02-15 16:28:38 +00:00
commit 0e793197bf
24 changed files with 3268 additions and 0 deletions

25
doc/README.md Normal file
View File

@@ -0,0 +1,25 @@
# Documentation Index
This directory contains project documentation for the Bitcoin Core Admin Dashboard.
## Document Map
- `doc/architecture.md` - System architecture, module responsibilities, and runtime flow.
- `doc/api.md` - HTTP API reference, authentication model, and endpoint contracts.
- `doc/data-models.md` - SQLite schema, data invariants, and retention behavior.
- `doc/build-and-deploy.md` - Build, runtime, and deployment rules for local and Docker usage.
- `doc/environment.md` - Environment variables, defaults, and production guidance.
- `doc/conventions.md` - Coding patterns and implementation conventions for this repository.
## Reading Order
1. Start with `doc/architecture.md`.
2. Use `doc/api.md` and `doc/data-models.md` for implementation details.
3. Use `doc/build-and-deploy.md` and `doc/environment.md` for operations.
4. Use `doc/conventions.md` when adding or modifying code.
## Cross-References
- API endpoints depend on persisted settings described in `doc/data-models.md`.
- Runtime behavior and deployment constraints are described in `doc/architecture.md` and `doc/build-and-deploy.md`.
- Configuration contracts in `doc/environment.md` are consumed by modules listed in `doc/architecture.md`.

133
doc/api.md Normal file
View File

@@ -0,0 +1,133 @@
# API Reference
## Base Behavior
- Base URL: same origin as UI host.
- UI route:
- `GET /` returns the dashboard HTML shell.
- API route namespace:
- All API endpoints are under `/api/...`.
- Cache behavior:
- Responses under `/api/` and `/static/` include no-store cache headers.
## Authentication Model
- Login endpoint sets a session value on success.
- Protected endpoints require `Depends(require_auth)`.
- Session cookie behavior:
- `same_site=lax`
- max age: 8 hours
- secure flag controlled by `SESSION_COOKIE_SECURE`
Related references:
- Configuration: `doc/environment.md`
- Auth implementation: `app/auth.py`
## Error Contract
- `401` for authentication failures.
- `400` for missing required node configuration values.
- `422` for request validation and unsupported chart window values.
- `502` for RPC or SSH operation failures.
- Error shape for handled exceptions:
- `{ "detail": "<message>" }`
## Endpoint Contracts
### Health and Session
| Method | Path | Auth | Description |
| --- | --- | --- | --- |
| `GET` | `/api/health` | No | Returns service liveness: `{ "ok": true }`. |
| `GET` | `/api/auth/me` | No | Returns current authentication state and username if authenticated. |
| `POST` | `/api/auth/login` | No | Validates credentials and creates session. |
| `POST` | `/api/auth/logout` | Yes | Clears session. |
`POST /api/auth/login` request body:
```json
{
"username": "admin",
"password": "secret"
}
```
### Node Settings
| Method | Path | Auth | Description |
| --- | --- | --- | --- |
| `GET` | `/api/settings` | Yes | Returns persisted node connection and control settings. |
| `PUT` | `/api/settings` | Yes | Validates and persists node settings. |
`PUT /api/settings` request body fields:
- `rpc_url` string
- `rpc_username` string
- `rpc_password` string
- `rpc_wallet` string
- `config_path` string
- `ssh_host` string
- `ssh_port` integer `1..65535`
- `ssh_username` string
- `ssh_password` string
- `ssh_key_path` string
- `bitcoin_binary` string
All string fields are trimmed before persistence.
### Dashboard
| Method | Path | Auth | Description |
| --- | --- | --- | --- |
| `GET` | `/api/dashboard/summary` | Yes | Returns chain, network, mempool, mining, uptime, optional wallet data, and `updated_at`. |
| `GET` | `/api/dashboard/history` | Yes | Returns metric history for query-param `window` and `limit`. |
| `GET` | `/api/dashboard/history/{window}` | Yes | Same as above with `window` as a path parameter. |
Supported `window` values:
- `5m`
- `30m`
- `2h`
- `6h`
- `all`
`limit` is clamped server-side to `[1, 20000]`.
### RPC Explorer
| Method | Path | Auth | Description |
| --- | --- | --- | --- |
| `POST` | `/api/rpc/call` | Yes | Executes a JSON-RPC method with params and optional wallet override. |
| `GET` | `/api/rpc/commands` | Yes | Calls node `help` and returns parsed method catalog. |
| `GET` | `/api/rpc/help/{method_name}` | Yes | Returns detailed `help` output for one method. |
`POST /api/rpc/call` request body:
```json
{
"method": "getblockchaininfo",
"params": [],
"wallet": null
}
```
### Node Actions
| Method | Path | Auth | Description |
| --- | --- | --- | --- |
| `POST` | `/api/actions/stop` | Yes | Calls RPC `stop`. |
| `POST` | `/api/actions/start` | Yes | Runs SSH start command built from persisted settings. |
| `POST` | `/api/actions/restart` | Yes | Attempts RPC stop then runs SSH start command. |
Action responses include action metadata and remote execution payload when SSH is involved.
## Operational Notes
- Startup and restart require:
- valid SSH connectivity
- `config_path`
- SSH user credentials or key access
- When running in Docker, `rpc_url` should usually target `host.docker.internal` if the node is on the Docker host.
See `doc/build-and-deploy.md` for Docker network details.

98
doc/architecture.md Normal file
View File

@@ -0,0 +1,98 @@
# Architecture
## Overview
The project is a single-service web application that provides operational control and observability for a remote Bitcoin Core node.
Core runtime components:
- FastAPI server for UI and API delivery.
- Session-based authentication middleware.
- SQLite persistence for node settings and sampled metrics.
- Bitcoin Core JSON-RPC client for node data and RPC execution.
- SSH command runner for daemon start and restart operations.
- Vanilla JavaScript frontend with Chart.js visualizations.
Related references:
- API contracts: `doc/api.md`
- Persistence schema: `doc/data-models.md`
- Deployment rules: `doc/build-and-deploy.md`
## Component Boundaries
### Backend Modules
- `app/main.py`
- Composes FastAPI app, middleware, startup/shutdown hooks, and all route handlers.
- Validates payloads via Pydantic models.
- Aggregates dashboard summary data and persists metric samples.
- Starts a background sampler thread that periodically stores metrics.
- `app/auth.py`
- Verifies login credentials.
- Enforces authenticated API access through `require_auth`.
- `app/config.py`
- Loads and validates environment-driven runtime configuration.
- Creates the data directory when needed.
- `app/db.py`
- Initializes schema.
- Persists and reads node settings.
- Persists and reads metric history with retention trimming.
- `app/bitcoin_rpc.py`
- Encapsulates JSON-RPC request and batch request behavior.
- Normalizes RPC errors through `BitcoinRPCError`.
- Parses `help` output into command catalog entries.
- `app/ssh_control.py`
- Resolves SSH host and connection parameters.
- Executes remote commands and returns execution result payloads.
- Builds safe daemon start command strings.
### Frontend Assets
- `app/templates/index.html`
- Declares login, dashboard, settings modal, control actions, and RPC explorer regions.
- `app/static/app.js`
- Owns client state, auth transitions, API calls, chart hydration, and polling.
- Caches metric history in browser local storage.
- `app/static/styles.css`
- Provides responsive layout and UI styling.
## Runtime Flow
1. Application startup calls `init_db()` and starts the metrics sampler thread.
2. Browser loads `/`, then frontend checks `/api/auth/me`.
3. After login, frontend loads settings, RPC command catalog, history, and summary.
4. Frontend refreshes summary every 15 seconds while page is open.
5. Backend stores sampled metrics:
- on each summary API call.
- in the background sampler at `METRICS_SAMPLER_INTERVAL_SECONDS` cadence.
## Data and Control Paths
- Read-only node visibility:
- `/api/dashboard/summary` uses batch RPC to fetch chain, network, mempool, mining, and uptime data.
- `/api/dashboard/history` and `/api/dashboard/history/{window}` read persisted metric points.
- Node management:
- Stop uses Bitcoin RPC `stop`.
- Start uses SSH execution of `bitcoind -daemon -conf=<config_path>`.
- Restart attempts RPC stop, waits briefly, then uses SSH start.
- RPC explorer:
- Command catalog derives from `help` output.
- Arbitrary RPC call endpoint accepts method and JSON-array params.
## Error and Resilience Model
- `BitcoinRPCError` and `SSHControlError` are mapped to HTTP `502`.
- Missing required node settings are surfaced as HTTP `400`.
- Validation issues return HTTP `422`.
- Background sampler is best-effort and suppresses internal exceptions to avoid service interruption.
## Security Model
- Authentication is session-cookie based using `SessionMiddleware`.
- Access to operational endpoints requires `Depends(require_auth)`.
- Credential verification supports:
- plaintext password via `APP_PASSWORD`
- bcrypt hash via `APP_PASSWORD_HASH`
See `doc/environment.md` for security-relevant configuration fields.

85
doc/build-and-deploy.md Normal file
View File

@@ -0,0 +1,85 @@
# Build and Deployment
## Runtime Targets
- Local Python process using Uvicorn.
- Docker container via `Dockerfile`.
- Docker Compose service via `docker-compose.yml`.
Related references:
- Configuration fields: `doc/environment.md`
- Architecture: `doc/architecture.md`
## Local Run
Typical local launch command:
```bash
uvicorn app.main:app --reload --port 8080
```
Service endpoint:
- `http://localhost:8080`
Notes:
- The app reads environment variables at startup.
- Database parent directory is created automatically.
## Docker Image Build Rules
Build file: `Dockerfile`
Current build behavior:
1. Base image: `python:3.12-slim`
2. Install Python dependencies from `requirements.txt`
3. Copy `app/` and `data/` into image
4. Expose container port `8080`
5. Start with Uvicorn on `0.0.0.0:8080`
## Docker Compose Rules
Compose file: `docker-compose.yml`
Dashboard service settings:
- Publishes `8080:8080`
- Loads environment from `.env`
- Sets:
- `DATA_DIR=/app/data`
- `DB_PATH=/app/data/dashboard.db`
- Mounts volume:
- `./data:/app/data`
- Adds host gateway alias:
- `host.docker.internal:host-gateway`
- Restart policy:
- `unless-stopped`
## Persistence Rules
- Persist `./data` on the host to retain:
- node settings
- chart metric history
- If `data/dashboard.db` is not persisted, settings and history reset when the container is recreated.
## Network Rules
If the Bitcoin node runs on the Docker host:
- Prefer RPC URL `http://host.docker.internal:8332`.
If using remote node hosts:
- Set RPC URL directly to the node endpoint.
- Ensure SSH host/credentials are valid for start/restart actions.
## Deployment Checklist
1. Set strong `APP_PASSWORD` or provide `APP_PASSWORD_HASH`.
2. Set a long random `SESSION_SECRET`.
3. Set `SESSION_COOKIE_SECURE=true` behind HTTPS.
4. Persist the database volume.
5. Restrict dashboard network exposure to trusted operators.

54
doc/conventions.md Normal file
View File

@@ -0,0 +1,54 @@
# Conventions and Coding Patterns
This document defines repository conventions to preserve existing architecture and behavior.
## Module Boundaries
- Keep backend concerns separated by module:
- auth in `app/auth.py`
- configuration in `app/config.py`
- persistence in `app/db.py`
- RPC transport in `app/bitcoin_rpc.py`
- SSH control in `app/ssh_control.py`
- route composition in `app/main.py`
- Do not collapse these responsibilities into one file.
## API and Error Patterns
- Protected routes must use `Depends(require_auth)`.
- Domain errors should raise module-specific exceptions:
- `BitcoinRPCError`
- `SSHControlError`
- Domain exceptions are converted to HTTP `502` in centralized exception handlers.
- Input validation should use Pydantic models and explicit HTTP errors for invalid state.
## Persistence Patterns
- Access SQLite through `app/db.py` helpers.
- Keep node settings as a single upserted row with `id = 1`.
- Preserve metrics retention behavior:
- 30-day time window
- 20000 row cap
## Frontend Patterns
- Keep frontend implementation in vanilla JavaScript plus Chart.js.
- Maintain centralized client `state` object in `app/static/app.js`.
- Use existing API helpers (`api`, error handling, auth transitions).
- Keep responsive behavior aligned with current CSS breakpoints.
## Security and Operational Patterns
- Require authentication for all state-changing and node-control APIs.
- Keep SSH command composition shell-safe using quoting.
- Treat background sampling as best-effort to avoid impacting request flow.
## Documentation and Change Tracking
When behavior, contracts, or operations change:
1. Update affected docs in `/doc`.
2. Update `README.md` if repository landing information changes.
3. Record meaningful changes in `CHANGELOG.md` under `[Unreleased]`.
Reference index: `doc/README.md`.

108
doc/data-models.md Normal file
View File

@@ -0,0 +1,108 @@
# Data Models and Schema
## Storage Backend
- Database engine: SQLite.
- Default file path: `./data/dashboard.db`.
- Effective path is controlled by `DB_PATH`.
Related references:
- Configuration values: `doc/environment.md`
- Persistence implementation: `app/db.py`
## Tables
### `node_settings`
Single-row settings table used as the source of truth for RPC and SSH connectivity.
Schema fields:
- `id INTEGER PRIMARY KEY CHECK (id = 1)`
- `rpc_url TEXT NOT NULL`
- `rpc_username TEXT NOT NULL`
- `rpc_password TEXT NOT NULL`
- `rpc_wallet TEXT NOT NULL`
- `config_path TEXT NOT NULL`
- `ssh_host TEXT NOT NULL`
- `ssh_port INTEGER NOT NULL`
- `ssh_username TEXT NOT NULL`
- `ssh_password TEXT NOT NULL`
- `ssh_key_path TEXT NOT NULL`
- `bitcoin_binary TEXT NOT NULL`
- `updated_at TEXT NOT NULL` (UTC ISO 8601 string)
Invariants:
- Exactly one logical settings record exists with `id = 1`.
- `save_settings` performs an upsert.
- Missing settings row is represented by in-code defaults.
### `metrics_history`
Time-series table for chart data and historical dashboard views.
Schema fields:
- `ts INTEGER PRIMARY KEY` (Unix timestamp, seconds)
- `blocks INTEGER NOT NULL`
- `headers INTEGER NOT NULL`
- `mempool_bytes INTEGER NOT NULL`
- `peers INTEGER NOT NULL`
Index:
- `idx_metrics_history_ts` on `ts`
Invariants:
- A timestamp has at most one row.
- Inserts on duplicate timestamp overwrite existing point.
- Returned history is chronological for UI consumption.
## Retention and Limits
Retention behavior is enforced during each metric insert:
- Time retention: rows older than 30 days are deleted.
- Count retention: rows beyond the newest 20000 samples are deleted.
- Query limit input is clamped to `[1, 20000]`.
## Metric Sampling Sources
A metric point is composed from RPC responses:
- `blocks` and `headers` from `getblockchaininfo`
- `mempool_bytes` from `getmempoolinfo.bytes`
- `peers` from `getnetworkinfo.connections`
Points are written:
- On each `/api/dashboard/summary` request.
- In a background sampler loop at configured interval.
## Settings Defaults
Default values used when no persisted row exists:
- `rpc_url`: `http://127.0.0.1:8332`
- `rpc_username`: empty string
- `rpc_password`: empty string
- `rpc_wallet`: empty string
- `config_path`: empty string
- `ssh_host`: empty string
- `ssh_port`: `22`
- `ssh_username`: empty string
- `ssh_password`: empty string
- `ssh_key_path`: empty string
- `bitcoin_binary`: `bitcoind`
- `updated_at`: empty string
## Data Consumers
- `/api/settings` and `/api/settings` (PUT) read and write `node_settings`.
- `/api/dashboard/history*` reads `metrics_history`.
- `/api/dashboard/summary` and sampler thread write `metrics_history`.
See `doc/api.md` for endpoint-level contracts.

49
doc/environment.md Normal file
View File

@@ -0,0 +1,49 @@
# Environment Requirements
## Environment Loading
- Environment values are loaded from process environment.
- `.env` is supported through `python-dotenv` at app startup.
- Configuration is cached in memory after first load.
Source implementation: `app/config.py`
## Variables
| Variable | Default | Purpose |
| --- | --- | --- |
| `APP_USERNAME` | `admin` | Login username accepted by the dashboard. |
| `APP_PASSWORD` | `changeme` | Plaintext password used when hash is not provided. |
| `APP_PASSWORD_HASH` | unset | Bcrypt hash used instead of plaintext password when present. |
| `SESSION_SECRET` | `change-this-secret` | Session signing secret for cookie middleware. |
| `SESSION_COOKIE_SECURE` | `false` | When true, session cookie is sent only over HTTPS. |
| `RPC_TIMEOUT_SECONDS` | `15` | Timeout for Bitcoin RPC HTTP requests. |
| `METRICS_SAMPLER_INTERVAL_SECONDS` | `60` | Background metrics sampling interval in seconds. |
| `DATA_DIR` | `./data` | Data directory root. Created if missing. |
| `DB_PATH` | `./data/dashboard.db` | SQLite file path. Overrides default path under `DATA_DIR`. |
## Validation and Normalization Rules
- `METRICS_SAMPLER_INTERVAL_SECONDS` minimum is clamped to `15`.
- `SESSION_COOKIE_SECURE` accepts truthy values:
- `1`
- `true`
- `yes`
- `on`
- `DB_PATH` parent directory is created automatically when needed.
## Security Guidance
Production baseline:
1. Do not use default `APP_USERNAME`.
2. Use `APP_PASSWORD_HASH` instead of plaintext password where possible.
3. Use a long random `SESSION_SECRET`.
4. Set `SESSION_COOKIE_SECURE=true` when served over HTTPS.
5. Scope dashboard network access to trusted hosts only.
## Example `.env`
Reference example is provided in `.env.example`.
See `doc/build-and-deploy.md` for environment injection in Docker Compose.