diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index bba5ca4..0000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,15 +0,0 @@ -# Default owners -* @crmuncher - -# Standards + fleet (slower review) -/AGENTS.md @crmuncher -/ARCHITECTURE.md @crmuncher -/README.md @crmuncher -/STATUS.md @crmuncher -/PLAN.md @crmuncher -/ROADMAP.md @crmuncher -/DECISIONS.md @crmuncher - -# Sensitive paths -/.secrets/ @crmuncher -/backend/chain_vault/ @crmuncher diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 1b6a476..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,68 +0,0 @@ -# AGENTS.md β€” WalletPress - -> AI agent contract. Read this before touching anything in this repo. - -## Status -canonical Β· owner=crmuncher Β· last_updated=2026-07-02 - -## What This Repo Is -Wallet-as-a-service. Wallet engine, x402 service, chain vault, hosting. CLI for wallet management. Production-grade. - -## Type -`backend` Β· language=Python 3.12 + FastAPI - -## Where It Lives -- Source: `https://git.rugmunch.io/RugMunchMedia/walletpress` -- Deployed: Docker on Talos (/srv/walletpress/) -- Port: 8010 - -## Built With -- Standards: [git.rugmunch.io/RugMunchMedia/standards](https://git.rugmunch.io/RugMunchMedia/standards) -- Toolchain: [TOOLCHAIN.md](https://git.rugmunch.io/RugMunchMedia/standards/raw/branch/main/TOOLCHAIN.md) -- Conventions: [CONVENTIONS.md](https://git.rugmunch.io/RugMunchMedia/standards/raw/branch/main/CONVENTIONS.md) - -## Components -- `app/`: FastAPI source (adapters, agent, cli, core, plugins, routers) - -## Dependencies -- postgresql (data store) -- redis (cache + queue) - -## 🚨 CRITICAL RULES - -1. **No nested repos.** Don't commit other complete project trees inside this one. -2. **No secrets.** Never commit `.secrets/`, `.env`, `*.pem`, `*.key`, `*.crt`. Use gopass. -3. **No data blobs.** Don't commit `*.zip`, `*.parquet`, model weights, sqlite files. -4. **No broken files.** Shell heredoc accidents must be caught before commit. -5. **No duplicate scaffolds.** If a `backend/` or `frontend/` subdir exists at root, it's either THE app or bloat. -6. **Update STATUS.md before committing.** Builders must know where we are. -7. **Read DECISIONS.md before architectural changes.** Don't repeat rejected designs. - -## Commands -```bash -make install # install deps -make dev # dev server -make test # tests -make lint # ruff + format check -make typecheck # mypy strict -make security # bandit + gitleaks -make ci # full pipeline -make commit # conventional commit -make clean # remove build artifacts -``` - -## Architecture -See [ARCHITECTURE.md](ARCHITECTURE.md). Update it when components change. - -## Plan -[PLAN.md](PLAN.md) shows current sprint. Update weekly. - -## Status -[STATUS.md](STATUS.md) shows where we are RIGHT NOW. Update before each commit. - -## Owners -- Primary: crmuncher -- Backup: TBD - -## Related Repos -See [standards/PRODUCTS.md](https://git.rugmunch.io/RugMunchMedia/standards/raw/branch/main/PRODUCTS.md). diff --git a/DECISIONS.md b/DECISIONS.md deleted file mode 100644 index e0fa692..0000000 --- a/DECISIONS.md +++ /dev/null @@ -1,27 +0,0 @@ -# DECISIONS.md β€” WalletPress - -> Architecture Decision Records (ADRs). Read before architectural changes. - -## What is an ADR? -A short document capturing an important architectural decision: -- Context (what's the problem?) -- Decision (what did we choose?) -- Consequences (what does this imply?) -- Alternatives (what else did we consider?) - -## How to Write a New ADR -1. Copy `docs/adr/0000-template.md` to `docs/adr/NNNN-short-title.md` -2. Fill in: Status, Context, Decision, Consequences, Alternatives -3. Add link below in "Index" -4. Commit as part of the change that triggered the ADR - -## Index - - -| Number | Title | Status | Date | -|--------|-------|--------|------| -| [0001](docs/adr/0001-initial-architecture.md) | Initial architecture | Accepted | 2026-07-02 | - -## Fleet-wide ADRs (in standards repo) -- [ADR-0001: Tailscale mesh as the only perimeter](https://git.rugmunch.io/RugMunchMedia/standards/raw/branch/main/MCP-ARCHITECTURE.md) -- [ADR-0012: 21-day sprint, not 10-week](https://git.rugmunch.io/RugMunchMedia/standards/raw/branch/main/FLEET-PLAN.md) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md deleted file mode 100644 index 1b047ea..0000000 --- a/DEPLOYMENT.md +++ /dev/null @@ -1,75 +0,0 @@ -# DEPLOYMENT.md β€” WalletPress - -> How to deploy. Build, push, restart, rollback. - -## Architecture -- Container: Docker -- Image registry: local (Talos) -- Orchestration: docker-compose on Talos -- Reverse proxy: nginx on Talos -- Auto-deploy: forgejo webhook β†’ Talos deploy script - -## Deploy - -### Automatic (CI/CD) -On merge to `main`: -1. forgejo webhook fires -2. Talos deploy script pulls latest commit -3. Rebuilds Docker image -4. Stops old container, starts new -5. Runs health check -6. Rolls back on failure - -### Manual -```bash -ssh netcup -cd /srv/walletpress -git pull origin main -docker build -t walletpress:latest . -docker stop walletpress -docker rm walletpress -docker run -d --name walletpress --restart unless-stopped --network host -p 8010:8010 walletpress:latest -``` - -## Health Check -```bash -curl -fsS http://localhost:8010/health -``` - -## Rollback -```bash -ssh netcup -cd /srv/walletpress -git log --oneline -5 # find last good commit -git checkout -docker build -t walletpress:latest . -docker restart walletpress -``` - -## Logs -```bash -docker logs walletpress --tail 100 -f -``` - -## Monitoring -- Prometheus: `/metrics` endpoint -- Grafana: dashboards in fleet-infra -- Loki: log aggregation - -## Secrets -All secrets in gopass: -- `rmi/walletpress/admin_key` -- `rmi/walletpress/db_password` -- _see AGENTS.md for full list_ - -Loaded via docker `--env-file` or systemd `EnvironmentFile`. - -## Firewall -- Public: 80, 443 (nginx) -- Internal: 8010 (localhost only, behind nginx) -- Tailscale: 8010 for admin access - -## Backup -- Code: forgejo (source of truth) + Hydra bare mirror (daily 04:00) -- Data: DB snapshot to R2 (daily) -- Config: gopass diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md deleted file mode 100644 index de42586..0000000 --- a/DEVELOPMENT.md +++ /dev/null @@ -1,83 +0,0 @@ -# DEVELOPMENT.md β€” WalletPress - -> Dev workflow. Install, code, test, commit, PR. - -## Setup - -### Prerequisites -- Python 3.12+ / Node 20+ -- `gopass` for secrets -- `mise` for tool version mgmt (or manual) -- `pre-commit` for hooks - -### Install -```bash -git clone https://git.rugmunch.io/RugMunchMedia/walletpress.git -cd walletpress -make install -pre-commit install -``` - -### Environment -```bash -# Required env vars (loaded from gopass on deploy, .env locally) -# See .env.example -cp .env.example .env -$EDITOR .env # fill in test values -``` - -## Workflow - -### 1. Create a branch -```bash -git checkout -b feat/my-feature -# or fix/my-bug, docs/my-doc, chore/my-chore -``` - -### 2. Make changes -- Write code -- Add tests -- Update docs (AGENTS.md, ARCHITECTURE.md, STATUS.md) - -### 3. Run pre-commit locally -```bash -make lint -make test -make typecheck -make security -``` - -### 4. Commit (conventional) -```bash -make commit # interactive -# or: -git commit -m "feat(scope): add new feature" -``` - -Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, ops, security - -### 5. Push + PR -```bash -git push -u origin feat/my-feature -# Open PR on forgejo: https://git.rugmunch.io/RugMunchMedia/walletpress/pulls/new -``` - -### 6. Wait for CI -- All checks must pass -- Review by CODEOWNERS -- Squash-merge to main -- Auto-deploys to Talos (via forgejo webhook) - -## Daily End-of-Day -```bash -make status # show what's changed -fleet-commit # commit helper with checklist -``` - -## Code Style -- Python: ruff (lint + format), mypy strict -- TypeScript: eslint + prettier -- Shell: shellcheck -- Markdown: vale - -See [standards/CONVENTIONS.md](https://git.rugmunch.io/RugMunchMedia/standards/raw/branch/main/CONVENTIONS.md). diff --git a/PLAN.md b/PLAN.md deleted file mode 100644 index 93997a6..0000000 --- a/PLAN.md +++ /dev/null @@ -1,39 +0,0 @@ -# PLAN.md β€” WalletPress - -> Current sprint. 21-day cycles per ADR-0012. - -## Status -current_sprint=2026-Q3-S1 Β· started=2026-07-01 Β· ends=2026-07-21 - -## Sprint Goals -1. _TODO: top 1-3 goals for this sprint_ - -## In Progress -- [ ] TODO: in-progress items - -## Backlog (this sprint) -- [ ] TODO: planned items - -## Risks -- TODO: anything blocking progress - -## Done (this sprint) -- _None yet β€” sprint just started._ - -## Carry-over from previous sprint -- _None._ - ---- - -## Definition of Done -- [ ] Code complete -- [ ] Tests written (>80% coverage on changed lines) -- [ ] Pre-commit hooks pass (ruff, mypy, gitleaks, bandit) -- [ ] CI passes on forgejo -- [ ] Conventional commit message -- [ ] PR reviewed + merged -- [ ] Deployed to staging -- [ ] Smoke-tested in production -- [ ] Observability in place (metrics, logs, alerts) -- [ ] Docs updated (AGENTS.md, ARCHITECTURE.md, README.md) -- [ ] STATUS.md updated diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md deleted file mode 100644 index 218f923..0000000 --- a/PR_DESCRIPTION.md +++ /dev/null @@ -1,45 +0,0 @@ -## Fixes walletpress backend deployment blockers - -The walletpress backend was unable to start due to 4 issues. This PR fixes all of them. - -### #1 β€” requirements.lock incomplete -**Problem:** `requirements.lock` was missing deps. The Dockerfile used `requirements.lock` for `pip install`, so the runtime couldn't import `python-multipart` (FastAPI Form data), `mcp` (MCP SDK), and various chain-specific deps. - -**Fix:** Switched Dockerfile to use `requirements.txt` (which has all loose pins) instead of the incomplete lock. Also added `python-multipart>=0.0.9` and `mcp>=1.25.0` to `requirements.txt`. - -### #2 β€” main.py wrong import name (AuditTrail) -**Problem:** `from core.audit import AuditTrail` β€” class doesn't exist; only `AuditLog` does. - -**Fix:** Changed import and usage to `AuditLog`. - -### #3 β€” main.py wrong import path (PersistentStore) -**Problem:** `from routers.chain_vault import _PersistentStore` β€” neither `_PersistentStore` nor `_persistent_store` exists in `chain_vault.py`. The actual class is `PersistentStore` (no underscore prefix) in `routers/_persistent_store.py`. - -**Fix:** Changed import to `from routers._persistent_store import PersistentStore`. - -### #4 β€” DB volume permissions -**Problem:** `/data/walletpress/walletpress.db` couldn't be opened (`unable to open database file`). The mounted volume had wrong ownership and the previous Dockerfile chown'd `/data` to walletpress user, but only at build time β€” the runtime mount overwrote this. - -**Fix:** Added `entrypoint.sh` that runs as root on container start, ensures `/data` exists, chowns to `walletpress:walletpress`, then `exec`s the CMD. Also fixed Dockerfile ordering (COPY entrypoint + chmod must happen before USER directive). - -### Verification -After all fixes: -``` -{"status":"ok","version":"1.1.0","service":"walletpress","checks":{"vault":"ok","key_store":"ok","proof":"ok","webhooks":"ok"}} -``` -- Container: `walletpress-backend` Up (healthy) -- API: 112 endpoints exposed at `/backend/*` via nginx -- Tailscale access works (no auth) -- Direct IP requires basic auth (401 without) - -### Test plan -```bash -# Tailnet -curl https://walletpress.rugmunch.io/backend/health -curl https://walletpress.rugmunch.io/backend/docs - -# Direct IP (needs creds) -curl -u crmuncher: https://152.53.80.39/backend/health -``` - -Closes the 4 blockers documented in fleet-infra/ADMIN-ACCESS.md. diff --git a/README.md b/README.md deleted file mode 100644 index 1af51f2..0000000 --- a/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# WalletPress - -> Part of **Rug Munch Media LLC** β€” Wyoming-based crypto intelligence platform. - -## What - -Wallet-as-a-service. Wallet engine, x402 service, chain vault, hosting. CLI for wallet management. Production-grade. - -## Type -`backend` Β· **Python 3.12 + FastAPI** - -## Quickstart -```bash -make install # install deps -make dev # start dev server -make test # run tests -make lint # lint + format -make ci # full CI pipeline -``` - -## Documentation -- [AGENTS.md](AGENTS.md) β€” AI agent contract -- [ARCHITECTURE.md](ARCHITECTURE.md) β€” technical architecture -- [PLAN.md](PLAN.md) β€” current sprint -- [ROADMAP.md](ROADMAP.md) β€” long-term direction -- [STATUS.md](STATUS.md) β€” where we are RIGHT NOW -- [DEVELOPMENT.md](DEVELOPMENT.md) β€” dev workflow -- [DEPLOYMENT.md](DEPLOYMENT.md) β€” how to deploy -- [TESTING.md](TESTING.md) β€” test strategy -- [SECURITY.md](SECURITY.md) β€” security model -- [DECISIONS.md](DECISIONS.md) β€” architecture decision records - -## Standards -This repo follows Rug Munch Media LLC fleet standards: -[git.rugmunch.io/RugMunchMedia/standards](https://git.rugmunch.io/RugMunchMedia/standards) - -## Repository -- Source of truth: `https://git.rugmunch.io/RugMunchMedia/walletpress` -- Mirrors: GitHub (LLC + personal), GitLab, HuggingFace -- Backup: Hydra daily at 04:00 via `git-backup.sh` diff --git a/STATUS.md b/STATUS.md deleted file mode 100644 index 20c57d8..0000000 --- a/STATUS.md +++ /dev/null @@ -1,36 +0,0 @@ -# STATUS.md β€” WalletPress - -> Where we are RIGHT NOW. Update before every commit. - -## Last Updated -2026-07-02 (template, replace on first commit) - -## Deployment Status -- **Last deploy**: TBD -- **Current version**: TBD -- **Health**: TBD -- **Uptime (30d)**: TBD -- **Active branch**: `main` - -## Open Issues -- _None β€” track in forgejo issues_ - -## Recent Activity -- 2026-07-02: Repository created from `fleet-template` - -## Known Issues / Tech Debt -- _None yet_ - -## Quick Links -- [Live URL](https://walletpress.rugmunch.io) -- [API docs](https://walletpress.rugmunch.io/docs) -- [Health](https://walletpress.rugmunch.io/health) -- [Repo](https://git.rugmunch.io/RugMunchMedia/walletpress) - -## Status Indicators -Use these in commit messages + status updates: -- 🟒 healthy β€” deployed, monitored, no issues -- 🟑 degraded β€” running but with known issues -- πŸ”΄ down β€” service unavailable -- 🚧 in-development β€” pre-production, expect breakage -- ⏸️ paused β€” work intentionally stopped diff --git a/TESTING.md b/TESTING.md deleted file mode 100644 index ee7c83a..0000000 --- a/TESTING.md +++ /dev/null @@ -1,73 +0,0 @@ -# TESTING.md β€” WalletPress - -> Test strategy. Unit, integration, e2e, fixtures. - -## Test Pyramid - -``` - /\ - / \ E2E (slow, few) - /----\ - / \ Integration (medium, more) - /--------\ - / \ Unit (fast, many) - /____________\ -``` - -- **Unit tests**: test individual functions/classes in isolation -- **Integration tests**: test components together (DB, API, auth) -- **E2E tests**: test full user flows (slow, run before deploy) - -## Structure -``` -tests/ -β”œβ”€β”€ unit/ # fast, no external deps -β”œβ”€β”€ integration/ # uses test DB, mocks external APIs -β”œβ”€β”€ e2e/ # full stack, runs against staging -β”œβ”€β”€ fixtures/ # test data (mainnet guard: no real addresses/keys) -└── conftest.py # pytest config -``` - -## Running - -```bash -make test # all tests -make test-unit # only unit -make test-integration # only integration -make test-cov # with coverage report -pytest tests/unit/test_X.py -v # single file -``` - -## Fixtures - -Use pytest fixtures. Examples: -- `clean_db` β€” fresh DB per test -- `mock_helius` β€” mock Helius API responses -- `auth_headers` β€” Bearer token for test user -- `sample_token` β€” fake token (test address, NOT mainnet) - -**NEVER use mainnet data in tests.** Pre-commit hook blocks this. - -## Coverage -- Target: >80% on changed lines -- Enforced in CI via `--cov-fail-under=80` - -## Mocking -- `httpx` for HTTP mocking (use `respx` or `pytest-httpx`) -- `unittest.mock` for general mocking -- VCR.py for recording/replaying API calls - -## Performance Tests -- `locust` for load testing (run against staging) -- Profile with `py-spy` or `cProfile` - -## CI -Every PR runs: -1. Lint (ruff) -2. Type check (mypy strict) -3. Unit tests + coverage -4. Integration tests (test DB) -5. Security scan (bandit, gitleaks) -6. Build Docker image (verify it compiles) - -Merge blocked if any fails. diff --git a/backend/Dockerfile b/backend/Dockerfile index 18c595b..a4716fa 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -2,43 +2,30 @@ FROM python:3.12-slim AS builder WORKDIR /app -RUN apt-get update && apt-get install -y --no-install-recommends gcc libffi-dev libssl-dev && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc libffi-dev libssl-dev && \ + rm -rf /var/lib/apt/lists/* -COPY requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt +COPY requirements.lock requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.lock FROM python:3.12-slim AS runner WORKDIR /app -# Install runtime deps (no gcc needed) -RUN apt-get update && apt-get install -y --no-install-recommends libssl-dev && rm -rf /var/lib/apt/lists/* - -# Copy Python deps from builder -COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages -COPY --from=builder /usr/local/bin /usr/local/bin - -# Copy app code -COPY . . - -# Create data dir + set perms (as root) -RUN mkdir -p /data && chmod 755 /data - -# Add entrypoint (as root) that fixes data perms then drops to walletpress user -COPY entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh - -# Create non-root user RUN groupadd -r walletpress && useradd -r -g walletpress -d /app -s /sbin/nologin walletpress -# Chown app + data dirs to walletpress -RUN chown -R walletpress:walletpress /app /data +COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages +COPY --from=builder /usr/local/bin /usr/local/bin +COPY . . + +RUN mkdir -p /data && chown walletpress:walletpress /data USER walletpress EXPOSE 8010 -HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 CMD python3 -c "import urllib.request; urllib.request.urlopen('http://localhost:8010/health')" || exit 1 +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD python3 -c "import urllib.request; urllib.request.urlopen('http://localhost:8010/health')" || exit 1 -ENTRYPOINT ["/entrypoint.sh"] CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8010"] diff --git a/backend/entrypoint.sh b/backend/entrypoint.sh deleted file mode 100755 index b6c6c5f..0000000 --- a/backend/entrypoint.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh -# Entry runs as root. Chown data dir then exec CMD as walletpress (per Dockerfile USER directive). -set -e - -mkdir -p /data -# chown needs to be done as root (current user) -chown -R walletpress:walletpress /data -chmod 755 /data - -# Print info -echo "[walletpress] data dir ready: /data" -echo "[walletpress] starting: $@" - -# Exec the CMD β€” runs as walletpress (Dockerfile USER directive) -exec "$@" diff --git a/backend/main.py b/backend/main.py index fbe87dc..9c8567d 100644 --- a/backend/main.py +++ b/backend/main.py @@ -108,13 +108,13 @@ async def lifespan(app: FastAPI): # Initialize services into app.state (enables DI and testability) from core.vault import Vault from core.auth import KeyStore - from core.audit import AuditLog + from core.audit import AuditTrail from core.license import LicenseManager from core.proof import ProofOfGeneration from wallet_engine.generator import WalletGenerator app.state.vault = Vault(cfg.db_path) app.state.key_store = KeyStore(cfg.keys_path) - app.state.audit = AuditLog(cfg.audit_path) + app.state.audit = AuditTrail(cfg.audit_path) app.state.license = LicenseManager() app.state.proof = ProofOfGeneration(cfg.data_dir / "proof.db") app.state.generator = WalletGenerator(vault_password=cfg.vault_password) @@ -122,8 +122,8 @@ async def lifespan(app: FastAPI): await app.state.webhook_deliverer.start() # Reload persisted webhooks into the live deliverer - from routers._persistent_store import PersistentStore - PersistentStore.reload_webhooks() + from routers.chain_vault import _PersistentStore + _PersistentStore.reload_webhooks() # Anchor source commit to Arweave for on-chain code verification if os.getenv("WP_POF_ARWEAVE_KEY_PATH"): diff --git a/backend/requirements.txt b/backend/requirements.txt index 2447644..21a93af 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -27,6 +27,3 @@ monero>=0.0.4 substrate-interface>=1.8.0 coincurve>=18.0.0 pycardano>=0.19.0 # Cardano Kholaw (BIP32-Ed25519 IOHK) β€” 1.1 MB disk - -python-multipart>=0.0.9 -mcp>=1.25.0 diff --git a/docs/adr/0000-template.md b/docs/adr/0000-template.md deleted file mode 100644 index aa0bdcc..0000000 --- a/docs/adr/0000-template.md +++ /dev/null @@ -1,16 +0,0 @@ -# ADR-NNNN: - -## Status -Proposed | Accepted | Deprecated | Superseded by ADR-XXXX - -## Context -What's the issue? What's the pressure for a decision? - -## Decision -What did we choose? - -## Consequences -What becomes easier? What becomes harder? - -## Alternatives -What else did we consider? Why didn't we pick them? diff --git a/docs/adr/0001-initial-architecture.md b/docs/adr/0001-initial-architecture.md deleted file mode 100644 index dd9cd96..0000000 --- a/docs/adr/0001-initial-architecture.md +++ /dev/null @@ -1,22 +0,0 @@ -# ADR-0001: Initial Architecture - -## Status -Accepted Β· 2026-07-02 - -## Context -First commit of WalletPress. Need to document the foundational choices. - -## Decision -- **Language**: Python 3.12 + FastAPI -- **Port**: 8010 -- **Deployment**: Docker on Talos behind nginx -- **Source of truth**: forgejo -- **CI**: forgejo Actions -- **Secrets**: gopass - -## Consequences -- All future decisions build on this foundation -- Breaking changes to these need a new ADR - -## Alternatives -- _TBD_