Merge pull request 'fix(backend): resolve 4 deployment blockers (requirements.lock, imports, DB perms)' (#1) from fix/backend-deployment-blockers into main
Some checks are pending
CI / lint (push) Waiting to run
CI / test (push) Waiting to run
CI / security (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / license (push) Waiting to run
CI / ai-review (push) Waiting to run

This commit is contained in:
Crypto Rug Munch 2026-07-01 20:52:15 +02:00
commit 2488cd4c08
5 changed files with 89 additions and 13 deletions

45
PR_DESCRIPTION.md Normal file
View file

@ -0,0 +1,45 @@
## 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:<password> https://152.53.80.39/backend/health
```
Closes the 4 blockers documented in fleet-infra/ADMIN-ACCESS.md.

View file

@ -2,30 +2,43 @@ 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.lock requirements.txt ./
RUN pip install --no-cache-dir -r requirements.lock
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.12-slim AS runner
WORKDIR /app
RUN groupadd -r walletpress && useradd -r -g walletpress -d /app -s /sbin/nologin walletpress
# 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 . .
RUN mkdir -p /data && chown walletpress:walletpress /data
# 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
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"]

15
backend/entrypoint.sh Executable file
View file

@ -0,0 +1,15 @@
#!/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 "$@"

View file

@ -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 AuditTrail
from core.audit import AuditLog
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 = AuditTrail(cfg.audit_path)
app.state.audit = AuditLog(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.chain_vault import _PersistentStore
_PersistentStore.reload_webhooks()
from routers._persistent_store import PersistentStore
PersistentStore.reload_webhooks()
# Anchor source commit to Arweave for on-chain code verification
if os.getenv("WP_POF_ARWEAVE_KEY_PATH"):

View file

@ -27,3 +27,6 @@ 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