AGENTS.MD
Guidance for AI coding agents working in this repository. Read this before making changes. FOLLOW GUIDANCE AS CLOSELY AS POSSIBLE. If you think the guidance is wrong, raise an issue or flag a maintainer — don't just do what you think is right. This is the source of truth for how we write code, tests, and docs in this repo.
Use these as the source of truth before exploring further:
PuterServer wiring, Context (ALS), and extensions.The backend is organized as a layered stack inspired by Controller–Service–Repository with dependency injection. Every layer only depends on the layers beneath it, and PuterServer (src/backend/server.ts) wires the whole thing together.
| Layer | Lives in | Responsibility |
|---|---|---|
| Controllers | src/backend/controllers/ | Route handlers. Parse + validate input, apply per-route gates (auth, subdomain, rate limit, body parsers via RouteOptions), call services, format responses. |
| Drivers | src/backend/drivers/ | Optional. RPC-style handlers exposed over /drivers/*. Thin shells that validate RPC inputs and call into services/stores. |
| Services | src/backend/services/ | Business logic. Assume the caller is already authenticated/authorized — services do not run auth gates themselves. |
| Stores | src/backend/stores/ | Persistence. Wraps clients with the domain shape services consume (rows, entities, KV namespaces). |
| Clients | src/backend/clients/ | Adapters for external/internal services (sql, redis, s3, dynamo, email, event bus). Knows protocols, not domain concepts. |
| Config | config.*.json → IConfig | Flat, typed config object every layer receives at construction. |
Each layer receives the layers beneath it through its constructor. Dependencies are explicit and traceable from PuterServer.
Context (ALS). Reach for Context only when the value is genuinely request-scoped and would otherwise thread through many layers. Today it's used sparingly — mostly for actor and req.Extensions live in extensions/ and parallel the layered stack. They are for non-crucial parts of the system — things Puter still works without if removed. If a feature is load-bearing for clients, it belongs in core, not in an extension (see whoami as the cautionary example).
The extension global (src/backend/extensions.ts) exposes:
extension.registerClient/Store/Service/Driver/Controller(name, Class) for first-class additions.extension.on(event, handler) and extension.get/post/put/delete/patch/use(path, opts?, handler) for the lightweight common case. opts is the same RouteOptions controllers use.extension.import('client' | 'store' | 'service' | 'controller' | 'driver') returns a lazy proxy to instantiated objects. extension.config exposes live config.PascalCase name. Don't hide types in random files where future readers won't grep them.camelCase for variables/functions, PascalCase for classes and for files containing a class (AuthService.ts, KVStoreDriver.ts).Keep comments light. Prefer self-documenting code — clear names, small functions, obvious flow. Add a comment only when:
Don't restate what the code already says. Don't write comments that reference the current task or PR — those rot.
When adding new behavior (function, endpoint, driver method, branch of logic), add a test for it.
*.test.ts or *.test.js.Before opening a PR, scan the diff for:
When in doubt, return less. Auth-, permission-, or data-export-related changes deserve an explicit callout in the PR description.