1ctx
AGENTS.md8 starsChanged 25 days agoApache-2.0View on GitHub
One continuous context for agents. Domain: 1ctx.dev. 1ctx is a self-hosted server where users chat with AI agents in projects and run them on schedules (automations), with shared knowledge and memory. - Runtime: Bun only, TypeScript run directly, one standalone binary. No Node. - Packages are devDependencies bundled at build time: exact pins, official npm only, bun install --ignore-scripts. A new package needs the user's explicit go-ahead. In src/, only server/lib/archive.ts imports @zip.js/zip.js and modern-tar, only server/email/smtp.ts imports nodemailer, only…
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,843 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository2 other files it gives its agents.
- external-tasksSkill
- visualizeSkill
The file
We split this AGENTS.md into the rules it gives an agent
7 rules. About 1 is common, 1 is distinctive, and 5 are very specific to this project.
≈1
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 195
No import cycles between files, type-only imports included.
- Very specific: line 200
Every stylesheet opens with @layer tokens, base, owners.
- Very specific: line 208
Only client/ui/Split.tsx writes the aside's line classes (split-line, split-item, split-note and their parts).
- Distinctive: line 213
No test names a real provider host.
- Very specific: line 220
Access is enforced in the router and nowhere else: Every route carries a policy: public, authenticated, admin or webhook.
- Common: line 254
Never a secret, message, prompt, description, tool input or output, query string, raw path, file name or text, email, attempted login name, …
- Very specific: line 279
UI copy is short and plain: No em-dashes anywhere. A send (one pass of the runner, docs/sessions.md) is a turn in a chat and a run in an …
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
1ctx
One continuous context for agents. Domain: 1ctx.dev. 1ctx is a self-hosted server where users chat with AI agents in projects and run them on schedules (automations), with shared knowledge and memory.
- Runtime: Bun only, TypeScript run directly, one standalone binary. No Node.
- Packages are devDependencies bundled at build time: exact pins,
official npm only,
bun install --ignore-scripts. A new package needs the user's explicit go-ahead. Insrc/, onlyserver/lib/archive.tsimports@zip.js/zip.jsandmodern-tar, onlyserver/email/smtp.tsimportsnodemailer, onlyserver/mcp-server/imports@modelcontextprotocol/server, and onlyclient/ui/Plot.tsximportsuplot.patches/holds modern-tar's patch: it keeps the raw headertypeflag, so GNU sparse and unknown types are not read as regular files, and cancelling an entry's body never drops the next entry's, which hung the stream. - just-bash is ours. Its source lives in
vendor/just-bash/and we change it;vendor/README.mdsays how. - Status: alpha. The API and the socket change freely, with no shims. Stored data is kept (see migrations below).
The dev loop
Everything goes through the Makefile; each target runs the
package.json script of the same name.
make preview # (re)start the local preview on 127.0.0.1:1236, hot reload
make preview-stop # stop it
make preview-log # tail its log
make preview-clean # stop it and wipe its db, secrets, log and pid
make preview-provision FILE=x.yaml # stop it, apply the objects, start it
make preview-reset FILE=x.yaml SECRETS=dir # wipe it, copy the secrets in, provision
make start ARGS="..." # run the source in the foreground with the server's flags
make dev ARGS="..." # the same with ONECTX_DEV=1 and a restart on server changes
make clean # preview-clean, then remove bin/ and Bun's build leftovers
make lint # biome check --write, then bun check; run after any code change
make test # bun test; run after any code change, before finishing
make vendor-test # just-bash's own suite, against its expected failures
make build # standalone binary in bin/
make smoke # start the binary, sign in over HTTP, stop it (CI runs it)
make image # the container image, native and loaded; PLATFORMS=a,b only builds
make image-smoke # run the image as production does, sign in, stop it (CI runs it)
make kind-up # the local kind cluster 1ctx-test; kind-image, kind-down
make staging-deploy # build main, back the staging db up, swap the binary, restart
make staging-provision FILE=x.yaml [SECRETS=dir] # stop staging, apply, start
make staging-status # what the staging service says
make load-db PRESET=bench|small # a load test database in scripts/load/out/
make load-bench ARGS="..." # compare builds under load; load-cluster on kind, load-summary
make models # refresh src/server/providers/models.json from models.devThe preview is a local, throwaway instance for trying a change. It runs
the source with ONECTX_DEV=1 against .preview/1ctx.sqlite and the
secrets in .preview/secrets/. A CSS edit hot-reloads, a client edit
reloads the page, a server edit restarts the process. The first start
writes user-admin.key with the password admin-preview. Check a UI
change in Chrome through the DevTools MCP at 1440 and 390 wide; the
console must stay empty. Report what was verified and how. Do not commit
unless asked.
Staging is a long-running instance on a separate machine, used for real
work, so its data is kept; docs/deploy.md says how it is run.
Layout
src/shared/ wire contracts and guards: contracts/, api/<area>.ts,
socket.ts, words.ts. No bun:, node:, DOM or package
imports. Only what crosses the wire, never a row.
src/server/ the binary. main.ts (flags, db, secrets) calls compose.ts,
the composition root: areas in layer order, their ports,
the route list, the router. lib/ (http, body, errors, log,
clock, ids, bus), db/ (open, transact, migrations/), then
one directory per area.
src/client/ the Preact app from client/index.html: app/ (routes,
router, shell), data/ (api, entity cache), lib/, ui/
(primitives, each with its stylesheet), transcript/,
composer/, feed/, agents/, views/<area>/, style/.
test/ invariants/ (cross-cutting suites), server/, client/,
shared/, vendor/just-bash/, structure.ts (the layout
rules), helpers/, fixtures/.
scripts/ preview, staging, smoke, image, release, vendor-test and
cloud-setup (a Claude Code cloud environment) scripts;
brand.py; record/, the fixture recorders, run by
hand; load/, the load harness (docs/load-testing.md).
skills/ installable agent skills, added by URL, never seeded;
the binary embeds and serves external-tasks/.
site/ 1ctx.dev and the brand files, its own project.
site/README.md is the brand book.
vendor/ just-bash/ (a git subtree, outside Biome and the structure
rules) and its three docs.
docs/ the rules of each area (see Docs).
deploy/ docker/ (the Docker Compose files), charts/onectx/ (the Helm
chart) and flux/ (its Flux example); docs/deploy.md.Server areas. src/server/<area>/ has index.ts (what others may
import, and the factory <area>Area(deps), the one place its store is
built), store.ts (<Noun>Store), routes.ts, parse.ts (the request
parsers) and named files for logic. A module declares each port it needs
as its own small interface; compose.ts passes what the factory of the
area that answers it returns for that port. A port to an area built
later is a closure called only after the list is complete. An edge the
layer order forbids is a port, never an import. A test of one area
builds it with its factory and fakes for its ports;
test/helpers/app.ts calls compose(), so the integration tests run
the binary's wiring.
Workers. A *.worker.ts is an extra entry of bun build --compile
in package.json's build script. Its URL is built in compose.ts,
since a relative URL inside the binary resolves against src/server.
Test helpers. test/helpers/app.ts wires the server over a test db
with a fake clock, the least argon2id cost, a cookie jar and a fake
fetch that answers only the recorded hosts (PROVIDER_URL,
GEMINI_URL, NIM_URL, GROQ_URL, KEV_URL, MLX_URL,
AZURE_URL, ANTHROPIC_URL) and fails every other, a scripted
CHECK_URL host (checkHost()), and a fake email sender that
keeps every message; sleeping, slept and commandPhase await the
fake clock and a command's phase. In chat.ts, until, idle,
freed, sessionSettled and ringing await bus events, sends
letting go and the fake clock's due sleeps. smtp.ts is a fake SMTP server on loopback.
chat.ts drives a chat with a scripted provider stream. auth-cases.ts
is the authorization matrix.
Docs
The files under docs/ and vendor/ are rules with the same force as
this file. Read the one that covers a change before making it, and
change it in the same commit as the code that changes a rule.
| Doc | Governs |
docs/ui.md | src/client/: data layer, primitives, forms, themes, helpers |
docs/shell.md | the shell, the rail and its Recent list, the drawer, the installed app, app links and /knowledge/ paths in replies |
docs/views.md | what a page draws: views/, the session list (feed) |
docs/project-settings.md | a project's repositories and MCP servers lists, on its Settings tab and admin page |
docs/channel-page.md | a project channel's page: quotes, pending turns, the context line, its pages and gap |
docs/composer.md | composer/: the draft, its files and switches, the slash commands |
docs/external-task-pages.md | what the external task pages draw: the tab, the task's page, its timeline, Home's card |
docs/admin-pages.md | what the admin pages draw: views/admin/, lists, object pages, drafts, saves |
docs/access.md | requests and the router, logins, users, names, project visibility, secrets, the socket, links by email |
docs/providers.md | providers/, deciders/, an agent's provider, model and thinking |
docs/wires.md | the azure and anthropic wires: their bodies, streams, catalogs and adapted refusals |
docs/sessions.md | sessions/ and the runner's sends (a chat's turns, a run): caps, writer, stream bounds |
docs/channel-context.md | a channel turn's snapshot, traces, stream budget, admission and meter, run posts, a clear |
docs/queue.md | messages to a busy chat: the queue, its dispatcher |
docs/subagents.md | the delegate tool, child sessions, the child loop and the child rows |
docs/compaction.md | the summary round, its max_tokens, the tail after a summary, compact on demand |
docs/archive.md | archive, agent retirement, packing, the sweep, the kept files job |
docs/memory.md | memory/, memory_edit, a run's memory phase |
docs/automations.md | automations/, the scheduler, the check URL, what only runs do |
docs/attention.md | the attention mark (agent, runner, decider backup), the open alert and its email |
docs/external-tasks.md | external-tasks/: member routes, task delivery and maintenance |
docs/tools.md | tools/, credentials/, skills/, limits/, the tool loop, visuals, email_user |
docs/project-tools.md | the automation and external_task tools |
docs/mcp.md | mcp/, MCP tools in a send, MCP results kept as files |
docs/mcp-server.md | mcp-server/, the inbound /mcp endpoint and its tools |
docs/knowledge.md | knowledge/, uploads |
docs/images.md | images/: the image store, ingest, its limits, serving, access, the lease, the agent's image flag, the claim at send, the budget, queued and forked images, image lines, the wires' image parts, the token estimate, the check, images in history, a message's images on the page |
docs/search.md | shared/search.ts, knowledge search, session search and list |
docs/repos.md | repos/: repositories, their hosts, names and credentials |
docs/email.md | email/: the SMTP server, the outbox, the sender, agent and alert email, the SMTP page, the pages of email links |
docs/dictation.md | dictation/: the Dictation setting, POST /api/transcribe, its WAV check, cap, call and usage row |
docs/bash.md | bash/, the bash tool, open, scratch, kept MCP files, curl signing |
docs/monitor.md | overview/: what the admin Monitor pages read, the usage windows |
docs/provision.md | provision/: applying YAML objects, --provision |
docs/deploy.md | service/, staging, the container image, deploy/, release and CI |
docs/load-testing.md | scripts/load/: the load harness, its fakes, databases, fence, targets and results |
vendor/README.md | changing or syncing vendor/just-bash/ |
vendor/changes.md | a hunk of vendor/just-bash/: its (1ctx <id>) entry, same commit |
vendor/differences.md | where a vendored command still differs from its tool |
Rules the structure test enforces
test/structure.ts is the source of truth; every rule has a rejected
fixture under test/fixtures/structure/.
shared/imports onlyshared/.client/importsclient/andshared/.server/importsclient/only inmain.ts, for the page.- Server areas have a layer order (
LAYERSin the test). An area imports only areas above it, through theirindex.tsorrules.ts.web/imports onlyaccessandlib, and nothing importsweb/,main.tsorcompose.ts. The server root holds onlymain.tsandcompose.ts, which may import every area. Moving an area is a change toLAYERSin the same commit. - A
rules.ts(an area's pure rules) or a*.worker.tsnever loadsdb/or an area'sindex.ts, so workers stay small (WORKER_EXEMPTIONSlists the exceptions). - No import cycles between files, type-only imports included. Dynamic
imports are string literals, never a template with
${}. Every import carries its extension. - A production file over 500 lines fails unless listed in
LINE_EXEMPTIONSwith a reason. - Every stylesheet opens with
@layer tokens, base, owners;and puts every rule in a layer (UNLAYEREDlists the exceptions, with reasons).style/tokens.cssandstyle/base.cssare unique names. Any other sheet has a unique name and owns that class prefix (rail.cssstyles.rail-*). It styles abase.cssprimitive only inside its own selector, and never selects by element, attribute, id or*at any depth, inside:is(),:has(),:where()and:not()too. - Only
client/ui/Split.tsxwrites the aside's line classes (split-line,split-item,split-noteand their parts). - Colour, font family, font size, the
fontshorthand and radius come only fromtokens.css: never a literal anywhere else, CSS or TSX, avar()fallback included (LITERAL_EXEMPTIONSlists the exceptions). - No test names a real provider host; the suite never reaches a network.
AGENTS.mdand eachdocs/*.mdstay underMAX_DOC_TOKENS(5,000 o200k tokens). A doc near the cap is cut, or split by topic with a row in the Docs table, never trimmed of a rule to fit.
Rules the code follows
- Access is enforced in the router and nowhere else. Every route
carries a policy:
public,authenticated,adminorwebhook. A handler gets a typedPrincipaland never reads a header. Every new route gets a row intest/helpers/auth-cases.ts, or the access suite fails. The rest of the rule (same origin, parsers, body caps, login limits) is indocs/access.md. - Routes never overlap. Two patterns of one method that could match
one path fail the router at start (
conflicts()inweb/router.ts). - Writes that belong together go through
transact(). The body returns its result and the bus events, published after the outermost commit and never on a throw, so nesting is safe. Bus events are hints; a subscriber reads rows for the truth. - A schema change is an appended migration, renames and retypes
included; a store never creates a table. A migration on
main, or one that ran on staging, is never edited, and no database is wiped: staging holds real data. Widening a check rebuilds the table in place (create, copy, drop, rename) and keeps the rows. - A rebuild of a referenced table sets
rebuild: trueand names its tables inrebuilds, copying every row with its key unchanged.migrate()turns foreign keys off around the transaction, since the pragma cannot change inside one and a drop would cascade, then throws when a named table's row count changed orforeign_key_checkfinds a row. - Secrets are files, one bare value per
<kind>-<name>.key, read through the secrets port bound to the caller's kind. A value is never logged, returned by a route or stored in the database (docs/access.md). - Session is the domain noun and never means a cookie; the cookie is a login.
- A log line is one slog text event on stderr, from
logger(area)inlib/log.ts. Messages are fixed lowercase phrases; fields are flat anddurationis whole milliseconds. Fields hold only ids, usernames, configured names and models, route patterns, counts, statuses, closed words, durations, client addresses, error fields and startup paths. Never a secret, message, prompt, description, tool input or output, query string, raw path, file name or text, email, attempted login name, body or socket reason. - Errors are logged through
errorFields(), which keeps the first line (cut at 200 characters, with live keys scrubbed) and the source frames. AToolErrorlogs its fixedloggedphrase, never its message. - The router logs a
requestfor a signed-in user's writes and 4xx answers and for any 5xx; a successfulkeyroute is the exception because its MCP tool logs the call. Never log an anonymous 4xx, health or ready. Refreshes and sweeps log only work done or a failure. A test collects events withcollectLogs(), passing itslogFactorytotestApp(). - Pure logic is separate from I/O and tested on fixtures; a bug is recorded as a fixture before it is fixed.
- Tests in a file run concurrently. A test that sets module state
(a signal,
globalThis.fetch) or counts bus events istest.serial. Linux CI runs the files in parallel, each in a fresh global, so a test sets what it reads itself. Local runs take the files one by one. A test's clock counts its neighbours' work, so the default timeout is 20 s. No test asserts elapsed time or lets a real sleep, timer or poll budget decide it: a fake clock, an awaited event or counted work. A promise that may never settle takes a plainawait, neverexpect(p).rejectsor.resolves, where Bun's timeout never fires. - Comments explain why, never what. Style is Biome's: 2 spaces, double quotes, semicolons, trailing commas, 80 columns.
- UI copy is short and plain. No em-dashes anywhere. A send (one
pass of the runner,
docs/sessions.md) is a turn in a chat and a run in an automation; the page never says send. perl -i -pefor global replaces,uv runfor ad hoc Python, never pip.- The brand SVGs and PNGs in
site/are generated: changescripts/brand.pyor the numbers insite/README.mdand regenerate.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

