AIDE Architecture — Phase 1: Backend Core
SkillFiles & storagePhase 1 SOP for the AIDE offline-first IDE rebuild, the modular daemon backend: HTTP router with zod .strict() validation at every route edge, fixed error envelope, workspace filesystem service with path containment, config/env validation, structured logging, process manager (spawn/execFile only, tree kill on Windows), health endpoint, graceful shutdown (children first). Use whenever writing backend routes/services, adding a workspace/file operation, debugging "route 500s", "path escape", or shutdown hangs, or auditing the daemon. Research-grounded (nodejs.org child_process docs, api-contract-testing.com, VS Code engineering docs).
Use AIDE Architecture — Phase 1: Backend Core in Claude, ChatGPT or Ahel Desktop
Free. Sign in, add AIDE Architecture — Phase 1: Backend Core and connect your AI. About a minute.
Also: Claude Code · Cursor · Codex
Then ask your AI: use the AIDE Architecture skill
Details
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; Ahel provides instructions and does not run this skill.
No other account needed.
Add Ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
What this skill tells your AI
The instructions your AI receives, as published by anonymousnomad/covert-coder in skills/packs/aide-arch-backend-core/SKILL.md and read by Ahel’s review.
Doctrine
Offline-first (localhost only, bind 127.0.0.1). Verify before claiming (route tests + live curl). Contract first (zod at the edge). Fail closed (reject unvalidated input, never crash the daemon). Full rebuild of the daemon: the old server.mjs single-router monolith is replaced by modular services; only verified engine pieces (model-manager gates, workspace-manager, training-manager, replay-store, groups) are ported as-is into the new structure.
What
Phase 1 delivers the daemon core every later phase plugs into:
- HTTP server (Node
node:http, no framework — or a minimal router; decision: keep Node http + a tiny route registry, matching the current stack; addwsin Phase 4). - Route registry pattern: each route =
{ method, path, schema, handler }. All request bodies validated withzod .strict().safeParse()BEFORE the handler runs; invalid → 400 error envelope. - Error envelope (from common/errors.ts):
{ ok: true, data }|{ ok: false, error: { code, message, detail? } }. Handlers NEVER throw raw — they return codes. - Workspace service:
list,read,write,stat,tree,mkdir,delete,search— ALL with path containment (see How #2). Ports the verified workspace-manager logic. - Config service: validates env + config file at startup (zod), fails fast with a clear message. Supports
AIDE_*env vars (e.g.AIDE_PYTHON,AIDE_WORKSPACE,AIDE_MODELS_DIR). - Process manager: spawn/execFile wrappers (NEVER exec), child registry, tree kill on Windows (
taskkill /PID x /T /Ffallback after graceful), used by later phases (model runtime, LSP, git, terminal, training). - Logging: structured JSON lines to
logs/daemon.log+ console; request log (method, path, status, ms); error log with stack; rotation by size (keep last N MB). - Health:
GET /api/health→{ ok: true, data: { version, uptime, workspace, freeMemoryMB } }(the freeMemory check is the RAM doctrine from Phase 6 — expose it early). - Graceful shutdown: SIGINT/SIGTERM → stop accepting → kill child processes (graceful-then-tree) → flush → exit 0. On Windows,
taskkillchildren explicitly; never orphan.
How
1. Route pattern (every route, no exceptions)
// common/contracts/file.ts
export const FileReadRequest = z.object({ path: z.string() }).strict();
export const FileReadResponse = z.object({ content: z.string().nullable(), tooLarge: z.boolean(), size: z.number() }).strict();
export type FileReadRequestT = z.infer<typeof FileReadRequest>;
// node/src/routes/file.ts
export const fileReadRoute: Route = {
method: 'GET', path: '/api/file',
schema: { query: FileReadRequest, response: FileReadResponse },
handler: async (ctx) => { /* ctx.validated, ctx.workspace */ }
};
ctx.validatedis the parsed output of the schema — handlers never touch raw query/body.- Response serialization:
FileReadResponse.parse(data)before send — egress validation, fail closed on both directions. - All routes return the envelope; the router wraps handlers so an unexpected throw becomes
{ ok:false, error:{ code:'INTERNAL', message } }with a logged stack (no crash, no raw leak).
2. Path containment (workspace service — security-critical)
- Every user-supplied path is resolved INSIDE the workspace root:
const root = path.resolve(workspace); const target = path.resolve(root, rel); if (target !== root && !target.startsWith(root + path.sep)) throw FORBIDDEN. - Never use the raw string; never trust
../. Test: attempt../secret, absolute path, drive-letter path, andworkspace\..\..— all must be rejected. - Symlink escape: optionally resolve realpath and re-check containment (Phase 7 git + extension installs are the risk surface; containment check mandatory there).
- Windows case-insensitivity: compare with
path.resolvenormalization AND lowercase the prefix check.
3. Process management (Windows reality, nodejs.org guidance)
spawn/execFilewith{ shell: false }ALWAYS (shell:true + user input = injection). Args as arrays, never interpolated strings.execFilefor one-shot commands (git, python -c checks) with timeout + maxBuffer + captured stderr.spawnfor long-running (model servers, LSP, training, terminal PTY).- Child registry:
Map<id, { child, kind, startedAt }>; every child gets astop(gracefulMs, forceAfterMs)= send SIGTERM/kill → wait →taskkill /PID <pid> /T /F(Windows tree kill — kills grandchildren; plain kill on Windows leaves orphan trees). - Daemon shutdown ALWAYS calls tree-kill on every registered child. Verified lesson: daemon restart took all llama python servers down — that was the tree kill working; the ORPHAN problem was daemons killed with
-Force(never do that except as last resort, and then huntpython -m llama_cpp.serverby command line afterward). - Never spawn with
detached: trueunless the process must survive the daemon (it must NOT — children belong to the daemon's lifecycle).
4. Logging + errors
- One logging module; levels debug/info/warn/error; JSON lines
{ ts, level, msg, ...meta }. - Error codes live in
common/errors.tsas a const union:BAD_REQUEST,FORBIDDEN,NOT_FOUND,CONFLICT,PAYLOAD_TOO_LARGE,INTERNAL,NOT_READY,TIMEOUT,CHILD_FAILED. Frontend switches on these codes (Phase 2) — they are part of the contract. - Never log secrets (model paths fine; auth tokens never — there are none offline).
5. Startup + config
config.tsvalidates: workspace root (exists? writable?), models dir, ports (default daemon 4777, UI 4173),AIDE_PYTHONoverride, log dir. Fail fast with a human message on first error.- Port conflict doctrine (verified 2026-08-16): if the daemon's port is taken, check if it's OUR daemon (health endpoint responds) → reuse or exit with message; if foreign (no health), pick the next free port and LOG it loudly. Never silently squat.
Why (research grounding)
- nodejs.org child_process docs:
shell: falsedefault is the injection barrier; spawn for streaming, execFile for one-shot with timeout. - api-contract-testing.com: validate at the network edge, single source of truth, egress validation — the frontend can never receive a shape the contract didn't allow.
- VS Code engineering: privileged hosts do ALL file/process work; the renderer never touches fs — this backend IS the privileged host.
- Verified project lessons (2026-08-16): python discovery chain must respect
AIDE_PYTHON→py -3.10 -E→py -3 -E→E:\Python310\python.exe -E(never barepython— MS Store stub); RAM doctrine: expose free memory early becauseMapViewOfFile failedkilled model runs below ~2GB free.
Dependencies
Node 20+ (ESM), zod (shared via common/), ws in Phase 4. All ported verified pieces: daemon/workspace-manager.mjs (search/replace logic incl. 20k occurrence cap, dotfile/node_modules skip), daemon/training-manager.mjs, daemon/replay-store.mjs, daemon/groups.mjs — ported, not rewritten.
Known issues / bugs (watch these)
- Windows tree kill: plain
child.kill()leaves grandchildren alive (llama python spawns no children, but git hooks and training scripts do). Always taskkill /T. Verify by PID scan after stop. - EADDRINUSE: bind errors must run the port-conflict check, not crash with a raw stack.
- Path separator bugs:
path.sepon Windows is\; containment checks that hardcode/fail. Use path.resolve everywhere. - Zod strict on query strings: query params arrive as strings; coerce with
z.coerce.number()etc. BEFORE strict parse, or valid requests get 400s. - Structured clone loss: never pass BigInt/undefined through JSON; the envelope is JSON-only (undefined → omit key).
- Request body size: cap body reads (e.g. 20MB) →
PAYLOAD_TOO_LARGE, or a giant POST will OOM the daemon. (File writes go through the file route with its own 1MiB gate per verified behavior — keep that gate semantics:{ tooLarge:true, size }.) - Backpressure: stream file reads for large files; the current app relies on the 1MiB gate for the editor — keep it; Monaco handles the rest in Phase 3.
- Graceful shutdown order: children FIRST, then HTTP close, then flush logs, then exit. Reversed order = killed children get a half-dead HTTP layer.
- Double-spawn protection: model/LSP/training start routes must be idempotent per kind (reject CONFLICT if already running) — the warmup-gate race (Phase 6) depends on it.
Phase 1 audit checklist (applied to the existing daemon)
- Old
server.mjsrouter decomposed intoroutes/+services/; every route validates with zod .strict() and returns the envelope. - Path containment enforced in every fs-touching route; escape tests exist and pass.
- Error codes from common/errors.ts used consistently; no raw throw escapes to JSON.
- Process manager with tree-kill exists and is USED by model/git/lsp/training; shutdown stops children first.
/api/healthexposes freeMemoryMB; daemon logs to logs/daemon.log with rotation.- Port-conflict doctrine implemented; config validates at startup with fail-fast.
- Ported pieces keep their verified behavior (search/replace cap, workspace search options, undo caps) — ported code must pass the SAME unit tests it passed before the move (test files move with the code).
npm run checkgreen including new route tests.
Signals
- GitHub stars
- 43
- Forks
- 14
- Last commit
- Oct 2026
Advanced
- Item type
- skill
- Key
aide-arch-backend-core- Source
- github.com/anonymousnomad/covert-coder
github.com/anonymousnomad/covert-coder
Related picks
Skill · davila7
The pick for C / C++legacy-js
Skill · thedaviddias
The pick for JavaScriptmodern-javascript-patterns
Skill · wshobson
The pick for JavaScriptpython-performance-optimization
Skill · wshobson
The pick for Pythonpython-pro
Skill · jeffallan
The pick for Pythonsetup-ts-deep-modules
Skill · mattpocock
The pick for TypeScript