This file is loaded into every Claude Code session in this repo. Keep it tight; durable conventions only, not transient state.
Self-hosted Docker management dashboard. Single binary, no build step, vanilla JS frontend, SQLite embedded. Two deployment modes: standalone (default) and HA (opt-in DD_MODE=ha, Redis-backed leader election).
Current version source of truth: src/version.js. Bump via npm version X.Y.Z (runs scripts/sync-version.js to propagate to docker-compose.yml + package.json).
- No build step. Frontend is plain JS loaded via
<script>tags. Don't add webpack/vite/esbuild. CSS is one filepublic/css/app.css. - No frontend framework. No React/Vue/Svelte. Plain DOM manipulation via objects with
render(container)methods. - CommonJS backend.
require()/module.exports. Don't introduce ESMimport. - SQLite embedded. No external DB. Migrations auto-apply at startup from
src/db/migrations/NNN_*.js. Numbering is monotonic — never reuse a number. - Audit trail mandatory for any state-changing action:
auditService.log({ userId, username, action, targetType, targetId, details, ip }). - RBAC mandatory on routes that touch state:
requireAuth+requireRole('admin')+writeablemiddleware chain. - Output escaping. Frontend uses
Utils.escapeHtml(value)on every interpolated user-facing string. Inlineonclick=in template strings is forbidden — useaddEventListenerafter rendering. - No secrets in source. AES-256-GCM via
src/utils/crypto.jsfor credentials at rest.ENCRYPTION_KEYenv var required at startup.
Strategic features ship in order: deep-spec → feature-spec → code → tests → release. Plans live in plans/ (gitignored, local-only). The discipline is non-negotiable for major features (everything that ships in a .0 minor): write the strategic intent before code, get acceptance, then execute.
Anti-features are documented WITH RATIONALE in each deep-spec's "OUT" section. If the user later asks "why didn't we ship X?", the deep-spec answers in writing.
Examples already in plans/ (gitignored, but referenced by CHANGELOG):
deep-spec-ai-features.md(v8.0.0)deep-spec-registry-hygiene-pack.md(v8.1.0)deep-spec-v8.2.0-pcloud-and-archives.md(v8.2.0)
- Port:
8101(HTTP) — referenced in.env.example,docker-compose.yml, README. Do NOT use3456(legacy from v5.x docs that may still appear). - Default admin:
admin/admin(forced password change on first login).c#12is the local dev override. - Test runner:
npm testruns Jest with--forceExit. CI lint is enforced as of v7.7.0 — fails on any warning. - Deploy targets: local (Docker Desktop), LAN (
192.168.13.20, userlocaladmin-a), public VPS (89.37.212.66, userroot). Both remotes use SSH key auth (no password). Seememory/server_deploy.mdfor the full sequence. - Build target: ALWAYS pass
--target productiontodocker build. Buildkit otherwise tries thedevelopmentstage in parallel and fails onnpm installif dev-deps registry is unreachable.
- Frontend pages > 1500 lines should split via the
containers.js+container-detail.jslazy-merge pattern (see v6.16.0 architecture). For non-lazy splits (pure organisational), see the v8.2.xsystem-egress.jsextract. - Backend route files > 2000 lines should split by sub-resource.
- Migrations should NOT contain bulk content (howto guides, template seeds). Use
src/db/howto-content/<slug>.md(with optional.ro.mdcompanion) and the import-at-startup loader (src/services/howto-loader.js) instead.
When the server starts, this is the order of operations on howto_guides:
- Migrations 040, 041, 042, 048, 050, 052, 053, 055, 058-062 INSERT initial built-in rows (historical seed).
howto-loader.jswalkssrc/db/howto-content/, parses each.mdfile's YAML front-matter, UPSERTS intohowto_guides. Markdown content overrides the migration content for any matching slug.
Practical rule: to add or edit a how-to, drop a markdown file. Don't touch migrations. The 132 markdown files committed in v8.2.x are the canonical source for the 66 howtos that have body content; migrations remain only as the schema-of-record + safety net for fresh installs that have no markdown directory mounted (which shouldn't happen with the production image, but the redundancy is cheap).
To migrate a how-to from migration to markdown: drop src/db/howto-content/<slug>.md with the same slug as the migration's INSERT. Loader UPSERTs, migration's content stops being the source of truth.
feat: / fix: / refactor: / chore: / docs: prefix + scope. Sign-off with the Co-Authored-By: Claude trailer when the session is AI-assisted. Examples in recent git log.
- Bump version:
npm version X.Y.Z(auto-syncssrc/version.js,docker-compose.yml, stages files). - Update
CHANGELOG.md(most-recent at top). - Update
public/js/pages/whatsnew.js_releasesarray (most-recent at top). - Commit + tag:
git tag vX.Y.Z && git push origin main --tags. - GitHub release with
gh release create vX.Y.Z --latest(the--latestflag matters — without it, GitHub UI shows older releases as "Latest"). - Deploy to all 3 targets (local + LAN + VPS) —
--target productionon remotes.
- Read tool may be blocked if
claude-memplugin hook fails. If you see PreToolUse hook errors, fall back toBashwithcat/seduntil the user fixes the hook. - Permission
setup_completedflag in DB defaults tofalse. Fresh deploys show "Initial Security Setup" modal. To skip for screenshot scripts:UPDATE settings SET value='true' WHERE key='setup_completed'; UPDATE users SET must_change_password=0 WHERE username='admin';.
| Need | Open |
|---|---|
| What's planned next | plans/ (gitignored) |
| What shipped recently | CHANGELOG.md first 200 lines |
| Architecture trade-offs | SECURITY.md "Known Security Tradeoffs" + docs/features/*.md |
| User-facing copy + i18n keys | public/js/i18n/en.js (canonical) + public/js/i18n/<lang>.js |
| RBAC matrix | src/middleware/auth.js — requireAuth, requireRole, writeable |
| Audit actions enum | src/services/ai/features/audit-actions-list.js (174 entries as of v8.2.0) |