Citadel pins an install to a tag that is not in the release list, and refuses the obvious fallback
The operating layer for Claude Code + OpenAI Codex: persistent project memory, intent routing, safety hooks, cost telemetry, and parallel agent fleets.
At a glance
- What is it?
- An operating layer for two coding agents that routes requests, keeps repository state between sessions and records reviewable evidence. Its offline path is a three-file release trio plus a plan that must be kept outside the repository, because writing it inside would change the snapshot the plan is checked against. The manifest is one version ahead of the newest tag.
- Who is it for?
- Citadel's interesting decision is that it declines to be convenient in three specific places. The install pins an explicit release and tells you not to substitute a moving branch when the tag is missing, which means a documented command can fail rather than silently install something unpinned.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.
Star history
- SethGammon/Citadel930 ★ · +122
| Repository | 2026-08-01 | 2026-11-13 |
|---|---|---|
| SethGammon/Citadel | 808 | 930 |
122 Stars in the last 104 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The documented install pins a tag the release page does not list yet
Both install paths name one explicit version, and the document pre-empts the question of what to do if that version is missing.
The instruction is that if the pinned tag is not present on the releases page, do not substitute the moving default branch. That is an unusual thing to write, and it is the right instruction: the alternative is an install that silently becomes whatever the branch contains the day you run it, which for a layer that holds repository state and executable hooks is exactly the outcome you do not want.
The cost is that the documented commands currently name a version ahead of the newest tag in the release list. The two most recent tags are a patch and its predecessor from mid September, and the manifest declares a later version again. So a reader who copies the install command exactly may get a marketplace error rather than a working install, and the error will look like a network problem rather than a documentation timing problem.
The requirements are short and worth stating plainly: one of the two supported coding agents, Node.js 22 or newer, and a git repository. There is no package to install from a registry, and the manifest says so too, carrying a private flag and a not-published marker so it cannot be published by accident. The plugin marketplace is the distribution channel, and one of the four claims attached to the install section is that a project-local installation is removable.
The two install paths differ only in the marketplace command and a local scope flag, and both are meant to be run from the repository Citadel will manage.
A prompt you can paste in constrains the agent that performs the install
One of the two installation methods is text to copy into the agent rather than commands to run.
The pasted prompt describes what the tool is, then states the boundary the installer must preserve: install a named version from a named location using the runtime's own plugin marketplace, enable it for this repository, use project-local defaults, preserve removal evidence for every change, and do not clone the default branch or change shared configuration, sandbox settings, permissions, or user-wide settings without asking. It ends mid-sentence in the file as published.
This is the mechanism worth pausing on. The agent performing the installation is the same kind of agent that runs later with repository access, so the constraints are written for something that would otherwise optimise for a successful install. Naming the things not to touch, before the install happens, is the only moment those constraints are cheap.
The document then draws a line about ownership, and it is a clean one. The platform owns plugin acquisition and the trust decision about executable code. The tool owns bounded project state and recovery. Plans, digests, receipts and diagnostic checks stay available as evidence but are explicitly not chores on the normal path.
That split is what makes the two install styles consistent. The quick path delegates acquisition and trust to the host, and the high-assurance path is for people who want to make the trust decision themselves.
The plan artefact has to live outside the repository it governs
There is a longer installation path, hidden behind a disclosure element, for offline and high-assurance use. It is four steps and then three commands.
The four steps are an explicit release page, not a branch. You download three files into one directory: the archive, a manifest, and a checksum sidecar. You compare the archive's digest against both published values before extracting anything, and a missing asset or a mismatch is described as a blocked install rather than a warning. Then you extract, and the single directory inside the archive becomes the immutable source for the adoption plan.
Those three files are referred to as a release trio, and the document uses the phrase again to explain why a source checkout is not equivalent: a clone of the default branch has no trio and no immutable version boundary, and must not be presented as a stable install.
The three commands are a plan, an apply and a doctor. The plan targets a directory, names the runtime projection, writes its output somewhere and prints JSON. The apply consumes that file and requires a confirmation token matching the plan. The doctor then checks the result. Runtime projection takes one of three values, and the both value is described as appropriate only when both projections are genuinely intended.
Then the rule that makes this design work:
node "$CITADEL_ROOT/scripts/adopt.js" plan "$CITADEL_ROOT" \
--target . --project-runtime codex \
--out ../citadel-adoption.plan.json --jsonEvery saved plan must be kept outside the target repository. Writing the plan inside the target changes the preflight snapshot the plan was made against, and the apply step will reject the mismatch with a named target-drift error.
A plan that contains a snapshot of a directory cannot live inside that directory without invalidating itself. The failure is detected rather than tolerated, and it is reported with a specific code instead of a generic refusal. The output path in the command above shows the shape of the requirement: a relative path that climbs out of the target before it writes anything.
A preview is incapable of running anything, by construction
The routing entry point takes a natural-language request, and the interesting behaviour is in what happens when you ask it to show you what it would do without doing it.
The preview mode shares exactly two things with the real thing: the exact-command check and the built-in candidate preflight. It does not inspect active state, does not discover project-local skills, and does not run the runtime language-model classifier. Every natural-language preview is non-executable, and the shape of the response is specified: the selected workflow is null, the command is null, a can-run-now flag is false, and the boundary is reported as requiring semantic classification.
That is a stronger guarantee than a warning would be. A preview that returns nulls cannot be mistaken for a dry run that might do something small, because there is no path from its output to an action.
The same strictness shows in how exact commands resolve. An exact command only matches when the normalized whole request matches, not when a prefix or a keyword does. And the three project-shaped commands, for testing, building and type checking, additionally require the target project to have the corresponding non-empty script in its package manifest, so a repository without a build script cannot be routed to a build.
There is also a validated override, which lets you name the route directly without bypassing activation or safety boundaries. That is the right shape for an escape hatch: it removes ambiguity about intent while leaving the checks in place.
Five public states, and one of them is a stop that names what it needs
The operating loop is described as five states, and the list is short enough to be worth reading as a design.
A request is where you describe an outcome. A run is where the selected workflow acts, inside runtime, repository and approval boundaries. Evidence is where checks and artefacts report one of four results: passed, failed, blocked, or unknown. And a resume state is where repository-local state names the next useful action for a fresh session.
The fifth is the one that sets this project apart. It is a stop state, and what it carries is the exact approval, conflict, or missing evidence required. Not a failure, not a generic permission request, but a named reason: the specific approval that is missing, the specific conflict detected, or the specific piece of evidence that would unblock the step.
That matters most when a run is long and spans sessions. A tool that ends a session with the reason it stopped gives the next session something to do. A tool that ends with an error gives it something to guess at. And the resume state, which persists repository-locally, is what turns the stop into progress rather than a restart.
The evidence vocabulary is deliberately small. Blocked and unknown are separate from failed, which is the difference between a check that said no and a check that could not answer.
The repository commits its own state directory and ships projections for six runtimes
The layout of the repository is the clearest description of the architecture available.
A state directory for the tool itself sits at the root, and so does an output directory. Both are committed. Whether that is deliberate dogfooding or a repository that forgot to ignore them, the effect is that a reader can see what the tool writes about this repository, which is either useful or embarrassing depending on the day.
Then the plugin manifests for both supported hosts, and a server configuration file that appears twice: once at the root and once inside one of the plugin directories. A planning directory holds templates and a rubrics subdirectory, which is the intake side of the loop.
The core directory is organised into about two dozen subdirectories whose names are the feature list: adoption, campaigns, coordination, contracts, evidence, intake, loops, map, memory, policy, security, skills, telemetry, verification and worktree among them. Reading those names tells you what the tool claims to do before you read a line of code.
A runtimes directory is the surprise. It holds projections for six hosts, including two the document never mentions: one for a hosted automation service, one for a local runtime, and two more beyond the two install paths it documents. So the shipping surface is wider than the documentation, which is normal and worth knowing if you are looking for a host that is already supported.
The published file list is hand-maintained at mixed granularity: some entries are whole directories, some are single files, and seven entries are explicit exclusions, four of which sit inside the telemetry directory and are named for measuring activation and traffic.
The document says what it does not replace
The last thing the overview says is a limit, and it is more useful than the feature list above it.
There is a table of situations and what gets added for each: repeated setup and lost context gets repository-local decisions and handoffs, an unclear workflow choice gets one natural-language entry point, risky or multi-step changes get approval boundaries and explicit verification, several agents or branches get isolated worktrees and shared discoveries, and work interrupted between sessions gets durable state and a concrete resume action.
Then the sentence that tells you when not to use it. For a short one-off edit, the coding agent you already have may be enough. And it does not replace the repository's instruction file, the agents instruction file, branch protection, or human review.
Four things declined, all of them either already present in a repository or not a tool's job. That is a short, specific, and checkable claim, and it is the sort of paragraph that makes the rest of the document more believable.
The document then opens one more section, about publishing positive and negative outcomes as evidence, and stops partway into its first sentence. The section is present in the file with its heading and an opening clause, and the rest of it is not there.
The release cadence behind all this is quick. Three releases inside two days in mid September, and a push to the default branch at the end of September.
Editorial conclusion
Citadel's interesting decision is that it declines to be convenient in three specific places. The install pins an explicit release and tells you not to substitute a moving branch when the tag is missing, which means a documented command can fail rather than silently install something unpinned. The paste-in prompt constrains the agent that performs the installation, so the boundary survives being automated. And the plan artefact has to live outside the repository, because putting it inside would invalidate its own drift check. Those are the parts worth reading. The cost is that the tool is opinionated about where things live, and the release documentation is one version ahead of what the visible release list contains, so verify the tag exists before starting. If your work is a short edit, the project's own note that your coding agent may already be enough is the right call, and it does not claim to replace a repository's instruction file, branch protection, or review by a person.
Frequently asked questions
What is Citadel?
An open-source operating layer for Claude Code and OpenAI Codex. It routes requests, preserves repository state between sessions, coordinates parallel work, applies repository safeguards, and records evidence and handoffs around the coding agent you already use. It requires one of the two agents, Node.js 22 or newer, and a git repository.
How do I install Citadel?
Through the plugin marketplace built into your agent, using the documented commands that pin an explicit release rather than a moving branch. The document also gives a manual offline path: download the archive, its manifest and its checksum sidecar, compare both published checksums before extracting, then run a plan, an apply with a confirmation token, and a doctor check. A source checkout is described as development-only.
Can Citadel preview a request without executing it?
Yes, and a preview is structurally incapable of running. It shares only the exact-command check and the built-in candidate preflight, does not inspect active state or run the language-model classifier, and returns a null selection and a null command with a flag saying it cannot run now, naming the boundary as requiring semantic classification.
What happens when Citadel needs something from you?
It stops in a named state and says what is required: the exact approval, the specific conflict, or the missing piece of evidence. That is one of five public states, alongside request, run, evidence and resume, where evidence reports passed, failed, blocked or unknown, and resume persists the next useful action for a fresh session.
Does Citadel replace my existing agent setup and repository rules?
It says it does not. The document states that for a short one-off edit your coding agent may already be enough, and that it does not replace CLAUDE.md, AGENTS.md, branch protection, or human review. It adds a natural-language entry point, repository-local state that survives sessions, and approval boundaries for risky or multi-step changes.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sethgammon-citadel)Star history chart for your README
[](https://hysenlabs.com/projects/sethgammon-citadel)Was this useful?