citio
Provides tools for codebase investigation, branch creation, pull request management, and CI status checking on GitHub repositories.
Uses OpenAI Codex (via a ChatGPT subscription) as the AI agent to execute engineering tasks.
Enables users to interact with the Citio AI engineering teammate via Slack, allowing task requests through DMs or channel mentions.
🤖 Citio
Your own AI engineering teammate, self-hosted, living in Slack.
@mention it or DM it and ask for real engineering work (investigate a bug, dig through CloudWatch logs, fix code, open a PR) and Citio runs Claude Code or OpenAI Codex inside your own infrastructure to do it. Slack is the interface, a controlled MCP tool layer is the safety boundary, and every credential stays in your own infrastructure.
No Team or Enterprise plan required. Citio runs on an individual Claude Max/Pro or ChatGPT Go/Plus/Pro (Codex) subscription, the agent you already pay for, now working from Slack.
Quickstart · How it works · Compare · Configuration · Customize · Architecture · Contributing · Security

A Slack message becomes an investigated, tested pull request, without leaving the thread.
✨ Why Citio
Most teams can already chat with an LLM. The harder problem is letting a team ask for real engineering work from Slack without handing a raw shell and a pile of credentials directly to the model.
Citio closes that gap:
💬 Slack is the user interface: DM the bot or
@mentionit in a channel.🧠 Claude Code or Codex is the execution engine: the provider CLI does the reasoning and planning.
🛡️ Citio is the control plane: it owns orchestration, session handling, repo setup, AWS/GitHub access, and a controlled MCP tool layer so the agent never touches raw credentials.
🏠 Everything runs in your infra: your container, your cloud account, your keys.
The result is something that can investigate bugs, inspect logs, edit code, and open pull requests, without a human sitting in the middle of every request.
Where it runs. Citio is a self-hosted container. This first version ships a native one-command AWS Fargate deploy (on Fargate Spot by default, ~$5–11/month, or
citio pauseto $0 when idle). Running on other hosts (a cheap VPS, Fly.io / Railway, or your own homelab) and a pay-per-use serverless mode are on the roadmap.
Related MCP server: Cloud Agent
🆚 Citio vs. hosted Slack agents
Anthropic's Claude Tag (June 2026) popularized exactly this idea (@mention an AI teammate in Slack and it does the work in-thread), but it's an Anthropic-hosted service gated to Claude Team and Enterprise plans, Claude-only. Citio takes the self-hosted, bring-your-own-subscription path:
Citio | Claude Tag | |
Hosting | Your AWS account, your infra | Anthropic-hosted SaaS |
Plan required | Individual Claude Max/Pro or ChatGPT Go/Plus/Pro | Claude Team or Enterprise |
Providers | Claude Code or OpenAI Codex | Claude only |
Credentials | Stay with you, behind an MCP allowlist | Managed by the vendor |
Best for | Solo devs & small teams who self-host | Orgs already on Team/Enterprise |
If you already pay for a Claude or ChatGPT subscription, Citio puts that same agent to work from Slack, no per-seat enterprise upgrade, no handing your code and credentials to someone else's cloud.
🧩 Features
🤝 Bring your own agent: Claude Code or OpenAI Codex, your subscription or API key.
🧰 Controlled MCP tools:
investigate_codebase,read_file,write_file,create_branch,create_pr,run_command(allowlisted),check_ci_status,query_logs,recall_context, and more.🔐 Credential boundary: the agent calls MCP tools; secrets live with Citio, not the model. Command execution is allowlisted and shell-metacharacter-rejected.
🧵 Slack-native: DMs and channel mentions, streamed progress, redacted output.
💾 Persistent workspace & memory: optional AWS EFS keeps repos, sessions, and provider auth across redeploys.
🪄 One-command installer: interactive setup wires up Slack, GitHub, provider auth, and deploys to ECS.
🏗️ How it works

Runtime shape:
A Slack request is normalized by the Slack adapter.
AgentRunner serializes work and manages provider sessions (one active task per container).
It spawns the Claude Code / Codex CLI as the agent, wired to Citio's MCP server via
--mcp-config.The agent uses MCP tools for codebase reads/writes, PR creation, log queries, and progress updates, never raw credentials.
Workspace, memory, and auth persist through EFS when enabled.
More detail: docs/ARCHITECTURE.md
🚀 Quickstart
Prerequisites
On your machine (the installer hard-checks for Docker, AWS CLI, and Git):
Tool | Version / note |
Node.js | ≥ 22 |
Docker | Running. The image is built |
AWS CLI | v2, authenticated with a profile that has the permissions below, see Setting up your AWS profile. |
Git | Any recent version. |
The agent CLIs (
claude,codex),gh, andjqship inside the container image: you don't install them on the host.
Accounts & tokens
An agent subscription: Claude Max/Pro, or ChatGPT Go/Plus/Pro for Codex (API key works as a fallback).
A Slack app (the installer can create it for you from a config token) + the target channel ID.
A GitHub fine-grained PAT with
contents: write+pull_requests: writeon the repos you want worked on.
Setting up your AWS profile
Citio deploys into your own AWS account. This takes about 10 minutes from "no AWS CLI" to "ready to install". If aws sts get-caller-identity already prints your account ID, skip to Permissions.
1. Install the AWS CLI
OS | Command |
macOS |
|
Ubuntu/Debian |
|
Windows |
Verify with aws --version (v2.x recommended).
2. Connect the CLI to your account
No AWS account yet? Create one at aws.amazon.com. Then pick one route:
Option A: IAM user + access key (simplest for a personal account)
AWS Console → IAM → Users → Create user (e.g.
citio-admin)Attach permissions (see Permissions below)
Open the user → Security credentials → Create access key → choose Command Line Interface (CLI)
Configure the profile:
aws configure --profile citio
# AWS Access Key ID: AKIA...
# AWS Secret Access Key: ...
# Default region name: eu-west-2 # any region you like
# Default output format: jsonOption B: IAM Identity Center / SSO (if your org uses it)
aws configure sso # follow the browser prompts
aws sso login --profile citioVerify either way: this must print your account ID:
aws sts get-caller-identity --profile citioThe installer lists your profiles automatically and re-runs this check before it touches anything.
Permissions
The deploy creates an ECR repository, an ECS cluster/service/task definition, an IAM task role, a security group, CloudWatch log groups, a Secrets Manager secret, and (optionally) an EFS filesystem.
Service | What Citio does with it |
ECR | Pushes the Citio container image to a private repo. |
ECS | Creates the cluster, task definition, and Fargate service that runs the agent. |
EC2 | Creates one security group in your default VPC. |
IAM | Creates the task role the container runs as (scoped to |
Secrets Manager | Stores your Slack / GitHub / provider tokens in |
CloudWatch Logs | Container logs, plus the agent's |
EFS (optional) | Persists agent credentials and workspace across restarts. |
Simplest (personal/sandbox account): attach the AWS-managed AdministratorAccess policy to your IAM user and skip the JSON below.
Least-privilege (shared or work account): attach this policy instead; it is scoped to exactly what the installer calls, and nothing more.
{
"Version": "2012-10-17",
"Statement": [
{ "Sid": "STS", "Effect": "Allow", "Action": ["sts:GetCallerIdentity"], "Resource": "*" },
{ "Sid": "ECR", "Effect": "Allow", "Action": ["ecr:GetAuthorizationToken", "ecr:CreateRepository", "ecr:DescribeRepositories", "ecr:BatchCheckLayerAvailability", "ecr:InitiateLayerUpload", "ecr:UploadLayerPart", "ecr:CompleteLayerUpload", "ecr:PutImage", "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer"], "Resource": "*" },
{ "Sid": "ECS", "Effect": "Allow", "Action": ["ecs:CreateCluster", "ecs:RegisterTaskDefinition", "ecs:CreateService", "ecs:UpdateService", "ecs:DescribeServices", "ecs:DescribeTasks", "ecs:ListTasks", "ecs:RunTask"], "Resource": "*" },
{ "Sid": "EFS", "Effect": "Allow", "Action": ["elasticfilesystem:CreateFileSystem", "elasticfilesystem:DescribeFileSystems", "elasticfilesystem:CreateMountTarget", "elasticfilesystem:DescribeMountTargets"], "Resource": "*" },
{ "Sid": "EC2", "Effect": "Allow", "Action": ["ec2:DescribeVpcs", "ec2:DescribeSubnets", "ec2:DescribeSecurityGroups", "ec2:CreateSecurityGroup", "ec2:AuthorizeSecurityGroupIngress"], "Resource": "*" },
{ "Sid": "Logs", "Effect": "Allow", "Action": ["logs:CreateLogGroup", "logs:DescribeLogGroups", "logs:GetLogEvents", "logs:FilterLogEvents", "logs:StartLiveTail"], "Resource": "*" },
{ "Sid": "Secrets", "Effect": "Allow", "Action": ["secretsmanager:CreateSecret", "secretsmanager:PutSecretValue", "secretsmanager:DescribeSecret", "secretsmanager:DeleteSecret"], "Resource": "arn:aws:secretsmanager:*:*:secret:citio/*" },
{ "Sid": "IAM", "Effect": "Allow", "Action": ["iam:CreateRole", "iam:GetRole", "iam:PutRolePolicy", "iam:AttachRolePolicy", "iam:PassRole"], "Resource": "arn:aws:iam::*:role/citio*" }
]
}The one people miss:
iam:PassRolescoped torole/citio*. Without it the ECS task can't assume the role the installer just created, and the deploy fails withAccessDenied.
Region
Use whichever region is closest to you. The installer auto-detects your CLI's default and offers it. All Citio resources land in one region; remember which, for teardown.
Install and run
Fastest: one command (uses the published package, no clone, no build):
npx @lawrencehui/citioInstalled globally (npm i -g @lawrencehui/citio) the command is just citio, citio status, citio destroy.
Or build from source (to read/modify the code first, or to contribute):
git clone https://github.com/lawrencehui/Citio.git
cd Citio
npm ci
npm run build
npm run initBoth launch the same guided installer, which will:
collect provider and auth settings (subscription OAuth first, API key as fallback)
collect Slack and GitHub credentials (stored in your OS keychain when available)
let you select which repos the agent can work on
write a local
citio.yamlbuild the image and deploy it to AWS ECS
💰 What it costs
Citio runs on Fargate Spot by default, roughly 70% cheaper than on-demand. Fargate bills per second, so cost tracks how long the task actually runs.
Task size ( | Spot (default) | On-demand | Good for |
0.5 vCPU / 1 GB ( | ~$5/mo | ~$18/mo | light/personal, small repos |
1 vCPU / 2 GB (default) | ~$11/mo | ~$36/mo | most use; bump memory if a big repo OOMs |
2 vCPU / 8 GB ( | ~$26/mo | ~$85/mo | large monorepos / heavy tasks |
Plus pennies for ECR storage and EFS (~$0.30/GB-mo). Not free-tier.
You rarely pay the monthly figure. Two ways to keep it near zero:
citio pause # scale to 0 tasks, compute charges stop, deployment + EFS stay
citio resume # back in ~1–2 min
citio destroy -- --yes --delete-efs # remove everythingA one-hour demo session costs well under $1.
Spot note: AWS can reclaim a Spot task (rare, 2-minute warning). Citio posts a "restarting, please re-send" notice and comes back automatically, fine for a single-instance bot. Want no interruptions? Set
deploy.aws.use_spot: falseincitio.yamlfor on-demand.
Teardown
citio destroy -- --yes --delete-efsOr by hand, if you'd rather see every call:
aws ecs update-service --cluster citio --service citio --desired-count 0
aws ecs delete-service --cluster citio --service citio
aws ecs delete-cluster --cluster citio
aws ecr delete-repository --repository-name citio --force
aws secretsmanager delete-secret --secret-id citio/runtime --force-delete-without-recovery
# if you enabled EFS (find the ID first):
aws efs describe-file-systems --creation-token citio-memory --query 'FileSystems[0].FileSystemId'
aws efs delete-file-system --file-system-id <fs-...> # delete mount targets first if prompted⚙️ Configuration
The installer generates a local citio.yaml. The committed citio.example.yaml shows the full shape:
name: citio
engine:
default_provider: claude # or "codex"
max_concurrent_sessions: 1
slack:
bot_token: ${SLACK_BOT_TOKEN}
app_token: ${SLACK_APP_TOKEN}
channel_id: C0123456789
workspace:
repos:
- url: https://github.com/your-org/your-repo.git
branch: main
rules:
- Always create PRs for code changes. Never push directly to main.
deploy:
provider: aws
aws:
region: eu-west-2
ecr_repo: citio # AWS resource names are yours to choose⚠️
citio.yamlholds local machine state (and is.gitignored). Don't commit it.
Runtime environment variables
Variable | Purpose |
| Path to the config file (default |
| Base64-encoded config (used by ECS, no file mount) |
| Workspace path (default |
| Memory/audit path (default |
🔐 Tokens are not stored as plaintext env vars. Your Slack, GitHub, and provider tokens are written to an AWS Secrets Manager secret (
citio/runtime) and injected into the container by ECS at start, so they are not readable viaecs:DescribeTaskDefinition. The values above are non-sensitive runtime config only.
🎛️ Customizing your instance
Yes, a Citio instance is configured almost entirely through citio.yaml (the installer writes it for you, and you can hand-edit then redeploy). The main knobs:
Setting | Where | What it controls |
Provider |
|
|
Agent rules |
| Plain-English guardrails injected into the agent ("always open PRs", "check logs before editing", your own policies) |
Repos |
| Which repos (and branches) the agent may clone and work on |
Who can use it |
| Restrict channel |
Session limits |
| How long a task can run; how many run at once (1 = strictly serialized) |
Skills |
| Optional community skill packs the agent can use |
Commit identity |
| Author on commits the agent makes |
Bot name | Slack app manifest (set at install) | The |
AWS sizing & names |
| Container resources and the names of the resources Citio provisions |
The fastest way to change behavior is usually workspace.rules: those instructions shape how the agent investigates, edits, and reports. After editing citio.yaml, re-run npm run init (or restart the container) to apply.
See citio.example.yaml for the full annotated shape.
🧱 Supported today
Area | Support |
Providers | Claude Code, OpenAI Codex |
Deploy | AWS ECS / Fargate, AWS ECR |
Persistence | Optional AWS EFS for workspace, memory, and provider auth |
Citio is currently AWS-first. Multi-cloud support is not part of the current public release.
🧪 Development
npm run typecheck # tsc --noEmit
npm run build # compile to dist/
npm run test # node:test suite
npm run dev # run locally with tsx📸 Screenshots
The PR Citio opened (real, reviewable work on GitHub):

Working in a channel: @mention it where your team already talks:

The installer: one guided command from zero to deployed:

🗺️ Status & roadmap
Citio is pre-1.0. This release deploys natively to AWS Fargate (Spot by default for low cost); the container itself is host-agnostic, so more deploy targets are coming.
✅ Slack-native control plane for Claude Code / Codex
✅ Controlled MCP tool layer with audit log
✅ One-command AWS Fargate installer (Fargate Spot default) with optional EFS persistence
✅
citio pause/citio resume/citio destroyfor cost control⏳ Not yet a hardened sandbox (provider CLIs retain native shell inside the container)
⏳ One active agent task per container
⏳ Native deploy target is AWS Fargate today, on the roadmap: run on any Docker host (VPS / Fly / Railway / homelab) and a pay-per-use serverless (Slack HTTP → Lambda → on-demand task) mode for ~$1–3/month
Known limitations
Citio is not a fully hardened multi-cloud platform yet. Treat this release as AWS-first and pre-1.0. Read this before deploying anywhere sensitive.
Security and isolation
Citio is a control plane, but the provider CLIs still retain native shell capabilities inside the container. The MCP tool layer is safer than handing an agent raw credentials, but it is not a policy-grade sandbox. Run it in an account you're willing to let an agent act in.
The installer stores secrets in your OS keychain when available, with a file fallback where no keychain backend exists.
Runtime and sessions
One active agent task runs at a time per container, intentional; the provider session is container-scoped.
Provider sessions don't survive a container restart or redeploy. Citio retries a failed resume as a fresh session, but provider-side conversation state is ephemeral.
Workspace state persists across redeploys only when EFS persistence is enabled.
Providers
Claude and Codex are both supported, but not symmetric: Claude uses
CLAUDE_CODE_OAUTH_TOKENor an API key; Codex OAuth depends on a persisted~/.codex/auth.json.Codex still relies on the CLI's native execution model, its surface isn't as clean as Claude's
--mcp-config.
Installer and deployment
The interactive installer is meant for a trusted operator machine, not CI/CD runners.
Local
citio.yamlis local machine state, don't commit it.
🧯 Troubleshooting
Symptom | Fix |
| Run |
|
|
| Your profile is missing the |
| Same cause: add the |
Docker push fails: | The installer logs into ECR for you. By hand: |
Deploy succeeds but Slack is silent | Check the app has |
Costs higher than expected | You may be on on-demand. Confirm |
🙌 Contributing
Contributions are welcome; see CONTRIBUTING.md. Keep diffs small, prefer runtime-safe behavior over clever abstractions, and don't commit local machine state.
🛡️ Security
How credentials are handled
Your Slack, GitHub, and provider tokens live in AWS Secrets Manager (
citio/runtime): never as plaintext task-definition environment variables, and never baked into the Docker image.The agent reaches your systems through the MCP tool layer, not by holding credentials itself.
run_commandis allowlisted and rejects shell metacharacters.Everything runs in your AWS account. No third party sees your code or tokens.
What Citio is not
It is not a hardened sandbox. The provider CLIs keep native shell access inside the container; see Known limitations. Deploy it into an account you're comfortable letting an agent act in.
Reporting
Found a vulnerability? Report it privately; see SECURITY.md. Please don't open a public issue for credential handling, auth bypass, shell injection, or sandbox escape.
📄 License
Maintenance
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/lawrencehui/Citio'
If you have feedback or need assistance with the MCP directory API, please join our Discord server