- adminAuth: exige Authorization: Bearer <HARDCAP_ADMIN_TOKEN> (o
X-Admin-Token). Comparación timingSafeEqual sobre sha256 de ambos
valores (sin leak de longitud ni timing). Fail-closed: sin token
configurado, TODAS las rutas admin responden 503.
- 401s ahora se loguean con método, ruta e IP (primera trazabilidad de
acceso que tiene el admin API desde que existe).
- token generado (32 bytes hex) y almacenado SOLO en
/srv/hardcap-backend/.env — nunca impreso ni versionado.
- test-admin-auth.mjs: 6 tests (sin token, incorrecto, vacío, correcto,
header alternativo, misma-longitud-distinto) — todos pasan en la VPS.
- desplegado en src + dist; servicio reiniciado y activo.
Nota de contexto: el admin API carecía de auth desde su creación
(index.ts: \x27TODO: add auth middleware before production\x27) y estuvo
alcanzable sin credenciales en :3001 hasta la contención de hoy.
- hardcap PATCH /admin/articles/:id → PUBLISHED now stages into
outbound_drafts (channel hardcap) instead of publishing, when
HARDCAP_PUBLISH_MODE=draft (default). Fail-closed. Verified E2E:
PATCH on a QUEUED article returned {staged:true, draft_id:7} and the
article stayed QUEUED.
- bitcoinhotels.org: audited — frontend only (Next.js), no publishing
path; nothing to contain.
- outbox.mjs: self-sufficient pool (createRequire from
/srv/cayce-core/node_modules/pg — hosts may lack pg; found by the
promo simulation which now works end-to-end).
- runtime/hardcap/admin.ts: versioned copy of the patched route
(hardcap-backend itself is not a git repo).
- deploy.sh: hardcap env wiring (HARDCAP_PUBLISH_MODE, IDENTITY_PATH,
CAYCE_PGURL appended server-side).
🔴 UNRELATED CRITICAL FINDING (needs operator decision, not patched
without approval): hardcap admin API has NO authentication and binds
*:3001 — reachable from the public internet (HTTP 200 from outside,
including DELETE /admin/articles/all). Recommend: auth middleware +
bind 127.0.0.1 + firewall, plus rotation of credentials that appeared
in journald (hardcap DB role DSN leaked via a psql error line during
testing — fixed in the same patch, but journald retains it).