Separate assistant workspaces
Configure context, memory and persona files for each assistant instance.
CortexForgev0.5.1
OpenClaw experiments · Docker Compose
Run separate OpenClaw assistants on a single host. Experiment with persona files, token budgets and messages between instances.
Python · MIT · Last update
Experimental and not production-safe. Do not use employee, customer, confidential or regulated data.
Docker Compose lab
admin ── quota-proxy ── model API
│
┌──────┴──────┐
persona A persona B
workspace workspace
└── message-broker ──┘
CortexForge
Configure context, memory and persona files for each assistant instance.
Test how the quota proxy records token usage and applies limits per instance.
Send messages through the broker and inspect each instance's inbox.
OpenClaw experiments · Docker Compose
Inspect Docker access, mounts and the repository setup guide before starting the sandbox.
Use disposable environments and synthetic content to explore the mechanics.
01 / Install
git clone https://github.com/rekurt/cortex-forge.git02 / README
Setup, examples and API reference.
Experimental / entertainment project. Do not use in production. CortexForge is a Docker Compose playground for trying isolated AI assistant instances, quota proxies, message routing, and personal personas. It is not audited, not hardened enough for real employee data, and includes intentionally powerful local-admin capabilities such as Docker socket access and project write mounts. Treat it as a lab, not an enterprise security product.
Language / Язык: English | Русский
CortexForge is an experimental AI-assistant infrastructure sandbox built on OpenClaw.
Project website · All projects by rekurt
The idea is simple: run several isolated Telegram-bot assistant instances on one host, each with its own workspace, persona, token quota, and message inbox.
This repository is useful for:
This repository is not suitable for:
CortexForge is split into a few small services:
| Component | Description |
|---|---|
| OpenClaw instances | One admin instance plus optional generated user instances |
| CLIProxyAPI | Optional OAuth proxy for Codex/Claude Max style access; stores its own OAuth session in a Docker volume |
| quota-proxy | Quota, rate-limit and audit proxy in front of the upstream model endpoint |
| message-broker | SQLite-backed inbox/message bus between instances |
| resource-monitor | Docker/container metrics and quota alerts |
| service-agent | Local HTTP API that runs approved skills as subprocesses |
The project started as a playful way to see what an "AI office full of little personal bots" could look like. It focuses on operational mechanics rather than polished product security:
| Experiment | Implementation |
|---|---|
| Per-instance context | Separate workspace, memory, Telegram token, and persona files |
| Quota accounting | quota-proxy stores monthly counters in SQLite |
| Admin control plane | Admin OpenClaw instance mounts the repo as /infra and can inspect Docker |
| Bot-to-bot messaging | message-broker exposes /send, /inbox, and admin inbox views |
| Skill execution | service-agent loads skill manifests and runs allowlisted subprocesses |
QUOTA_KEY_* values and call the local quota-proxySOUL.md)/infra and Docker accessmaster┌──────────────────────────────────────────────────────────────────┐
│ Docker Networks │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ user-1 │ │ user-2 │ │ user-3 │ ... corp-internal │
│ │ own bot │ │ own bot │ │ own bot │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ │ openclaw.json baseUrl=http://quota-proxy │
│ ┌────────▼─────────┐ │
│ │ CortexForge │ corp-internal │
│ │ Proxy :9090 │ corp-admin │
│ │ │ corp-egress ──► internet │
│ │ ✓ counts tokens │ │
│ │ ✓ enforces caps │ │
│ │ ✓ audit log │ │
│ └────────┬─────────┘ │
│ │ │
│ ▼ CLIProxyAPI or api.anthropic.com │
│ │
│ ┌───────────────────────┐ ┌──────────────────────────────┐ │
│ │ CortexForge Broker │ │ resource-monitor :9091 │ │
│ │ per-instance inbox │ │ CPU / RAM / disk │ │
│ │ auth by key │ │ alerts + HTTP API │ │
│ └───────────────────────┘ └──────────────────────────────┘ │
│ │
│ ┌───────────────────────┐ ┌──────────────────────────────┐ │
│ │ service-agent :8090 │ │ ADMIN — Admin 🏛️ │ │
│ │ /v1/run /v1/skills │ │ /infra access │ │
│ │ skills as subprocess │ │ quota / users / docker │ │
│ └───────────────────────┘ └──────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
openclaw.json; user templates point baseUrl to http://quota-proxy:9090quota-proxy checks the token quota, updates the counter in SQLite, then forwards the request to CLIProxyAPI by default (http://cliproxyapi:8317) or to a direct upstream if configuredquota-proxy returns HTTP 429 immediately — no real API call is made| Network | internal |
Connected to | Purpose |
|---|---|---|---|
corp-internal |
✅ yes | admin, generated instances, broker, quota-proxy, monitor, service-agent | internal data bus — no direct internet |
corp-admin |
✅ yes | admin, quota-proxy, monitor | quota management and metrics — no direct internet |
corp-egress |
❌ no | quota-proxy, CLIProxyAPI | upstream model/OAuth egress |
corp-services |
❌ no | service-agent | backend service calls |
corp-outbound |
❌ no | admin and generated instances | Telegram and other non-model external APIs |
Generated user instances are not placed on corp-admin and do not mount /infra. They do have corp-outbound for Telegram and other external calls, so this is not a strict air-gapped or production-grade isolation model.
| Component | Minimum | Recommended |
|---|---|---|
| OS | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
| CPU | 2 cores | 4+ cores |
| RAM | 4 GB | 8+ GB (~500 MB per instance) |
| Disk | 20 GB | 50+ GB |
| Docker | 24+ | latest |
| Docker Compose | 2.x | latest |
| Python | 3.12+ | 3.12+ |
| ShellCheck | optional locally | required for full local lint |
git clone https://github.com/example-org/cortex-forge.git /opt/CortexForge
cd /opt/CortexForge
cp .env.example .env
chmod 600 .env
nano .env
Minimum local values:
ANTHROPIC_API_KEY=sk-ant-... # used by the admin instance directly
CLIPROXY_API_KEY=clip-... # quota-proxy -> CLIProxyAPI auth
QUOTA_ADMIN_TOKEN=<random-32-chars> # quota-proxy admin API token
BROKER_KEY_ADMIN=<random-32-chars> # admin broker key
BROKER_KEY_SERVICE=<random-32-chars> # service-agent broker key
BROKER_KEY_MONITOR=<random-32-chars> # monitor broker key
MONITOR_ADMIN_TOKEN=<random-32-chars> # resource-monitor API token
SERVICE_API_KEY=svc-... # service-agent bearer token
Generate random tokens:
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
python3 -c "import secrets; print('clip-' + secrets.token_urlsafe(24))"
python3 -c "import secrets; print('svc-' + secrets.token_urlsafe(24))"
docker-compose.yml currently points quota-proxy at CLIProxyAPI:
UPSTREAM_URL=http://cliproxyapi:8317
UPSTREAM_API_KEY=${CLIPROXY_API_KEY}
That means CLIProxyAPI needs an OAuth credential stored in the cliproxyapi-auths Docker volume before normal user instances can call models through the proxy. The full headless login procedure is documented in CLAUDE.md. Without that OAuth state, the CLIProxyAPI container can restart cleanly but still be unusable for real model calls.
For a simpler local experiment, change UPSTREAM_URL to a direct provider endpoint and provide a compatible upstream key. Do not commit real credentials.
The admin instance is mandatory — docker-compose.yml references instances/admin/.env at startup. Without it, docker compose config will fail.
mkdir -p instances/admin
cp instances/.env.example instances/admin/.env
chmod 600 instances/admin/.env
nano instances/admin/.env
Required fields:
TELEGRAM_BOT_TOKEN=0000000000:CHANGE_ME_BOT_TOKEN
TELEGRAM_ALLOW_FROM=000000000 # your Telegram user ID
BROKER_KEY=<same as BROKER_KEY_ADMIN> # must match the global .env value
make security-check
All items should show ✅. Fix any ❌ before proceeding.
make add-user NAME=user-2 \
BOT_TOKEN=7000000000:AAxxxx \
FULL_NAME="Example User" \
TG_ID=123456789
This script automatically:
instances/user-2/ from the templateBROKER_KEY and QUOTA_KEY.envdocker-compose.override.yml (gitignored)Then optionally fill in personal credentials:
nano instances/user-2/.env # Yandex OAuth, GitLab token, etc.
nano instances/user-2/workspace/SOUL.md # persona style and character
make deploy
make status # all containers should be Up (healthy)
.env| Variable | Required | Description |
|---|---|---|
ANTHROPIC_API_KEY |
✅ | Anthropic API key used by the admin instance directly; generated instances should use quota keys through quota-proxy |
QUOTA_ADMIN_TOKEN |
✅ | Bearer token for quota management API |
QUOTA_DEFAULT_MONTHLY |
— | Default token limit per month (default: 1000000) |
BROKER_KEY_ADMIN |
✅ | Admin broker key |
MONITOR_ADMIN_TOKEN |
✅ | Bearer token for resource-monitor API |
SERVICE_API_KEY |
✅ | Bearer token for service-agent API |
ENRICH_PY_PATH |
— | Path to enrich.py on host for compliance skill |
DADATA_API_KEY |
— | DaData API key (for compliance checks) |
ALERT_CPU_PCT |
— | CPU alert threshold % (default: 80) |
ALERT_RAM_PCT |
— | RAM alert threshold % (default: 85) |
ALERT_DISK_PCT |
— | Disk alert threshold % (default: 90) |
.env (instances/<name>/.env)| Variable | Required | Description |
|---|---|---|
TELEGRAM_BOT_TOKEN |
✅ | Telegram bot token placeholder |
TELEGRAM_ALLOW_FROM |
✅ | Allowed Telegram ID(s), comma-separated |
BROKER_KEY |
✅ | Instance broker key (auto-generated by add-user.sh) |
YANDEX_OAUTH_TOKEN |
— | Yandex OAuth for mail / Tracker |
YANDEX_CALDAV_URL |
— | Yandex Calendar CalDAV URL |
YANDEX_USER |
— | Yandex username |
YANDEX_APP_PASSWORD |
— | Yandex app password |
GITLAB_TOKEN |
— | GitLab Personal Access Token |
| Command | Description |
|---|---|
make add-user NAME=x BOT_TOKEN=y TG_ID=z |
Onboard a new employee |
make remove-user NAME=x |
Remove instance (archives workspace to backups/ first) |
make deploy |
Start / rebuild all containers |
make restart NAME=x |
Restart a single instance |
make logs NAME=x |
Tail logs for an instance |
make status |
Status of all containers |
make backup |
Back up all workspaces to backups/ |
| Command | Description |
|---|---|
make quota-report |
Usage report for the current month |
make quota-report MONTH=2026-03 |
Report for a specific month |
make set-limit NAME=x LIMIT=500000 |
Set token quota (tokens/month), no restart needed |
make quota-reset NAME=x |
Reset current-month counter for an instance |
| Command | Description |
|---|---|
make monitor |
Current CPU / RAM / disk metrics |
make monitor-alerts |
Active alerts (1h cooldown per alert type) |
| Command | Description |
|---|---|
make service-health |
Service-agent healthcheck |
make service-skills |
List available skills with parameters |
make add-service NAME=x PORT=y SKILLS=z |
Add a service-agent replica |
| Command | Description |
|---|---|
make security-check |
Verify .env permissions, key presence, gitignore status |
Quotas are enforced by quota-proxy before a generated instance reaches the configured upstream (CLIProxyAPI by default, or direct API if reconfigured).
📊 Token report (2026-02):
user-1 823,451 / 1,000,000 tokens ⚠️ warning ████████████████
in=641,203 out=182,248 reqs=1,847
user-2 312,008 / 500,000 tokens ✅ ok ██████
in=241,500 out=70,508 reqs=892
user-3 45,100 / 500,000 tokens ✅ ok █
in=38,200 out=6,900 reqs=203
make quota-reset)make set-limit NAME=user-2 LIMIT=1000000Each instance's behavior is defined by two files in instances/<name>/workspace/:
SOUL.md — how the assistant thinks and communicates:
You are a precise, no-nonsense analyst.
Give facts, numbers, structure. No guessing.
If data is missing — say so directly.
IDENTITY.md — name, role, emoji:
- Name: Vector
- Emoji: 📐
- Vibe: accurate, dry, reliable
USER.md — context about the employee (role, timezone, preferences):
- Name: Example User
- Role: Backend Lead
- Timezone: UTC+3
The admin instance is named Admin 🏛️ (the head of a Capuchin monastery — he oversees all instances). Admin is the only instance with access to /infra, Docker control and quota management.
CortexForge/
├── cliproxyapi/ # CLIProxyAPI config and entrypoint for OAuth proxy mode
├── quota-proxy/ # quota/rate-limit/audit proxy for generated instances
│ ├── proxy.py # HTTP proxy + SQLite quota & audit log
│ └── Dockerfile
├── broker/ # message bus between assistants
│ └── broker.py # SQLite-persistent inbox per instance, auth by key
├── resource-monitor/ # CPU/RAM/disk monitoring
│ ├── monitor.py # metrics collection, alert cooldown, HTTP API
│ └── Dockerfile
├── service-agent/ # HTTP API for backend services
│ ├── server.py # /v1/run /v1/skills /v1/health /v1/usage
│ ├── skills/
│ │ └── compliance/ # compliance risk check (calls enrich.py)
│ └── Dockerfile
├── instances/
│ ├── admin/ # Admin — sole instance with /infra access
│ │ └── workspace/ # IDENTITY.md, SOUL.md, AGENTS.md
│ ├── _template/ # copied by add-user.sh for every new instance
│ └── .env.example # instance .env template
├── shared/
│ └── skills/
│ └── corp-messenger/ # skill for sending/reading broker messages
├── scripts/
│ ├── add-user.sh # onboarding: keys, template copy, docker-compose entry
│ ├── remove-user.sh # remove instance with workspace archive
│ ├── quota.sh # quota CLI (report / reset / set-limit)
│ ├── monitor.sh # monitoring CLI
│ ├── add-service.sh # add service-agent replica
│ └── backup.sh # workspace backup (tar.gz per instance)
├── docs/
│ ├── SETUP.md # server installation guide
│ ├── ONBOARDING.md # employee onboarding guide
│ ├── PERSONAS.md # persona creation guide
│ ├── MESSAGING.md # inter-instance messaging reference
│ ├── SERVICE_AGENT.md # service-agent API reference
│ └── SECURITY.md # threat model, CVE table, hardening
├── .github/
│ ├── workflows/
│ │ ├── security.yml # Gitleaks, Semgrep, Bandit, Trivy, Hadolint, ShellCheck
│ │ ├── release-please.yml # automated release PRs
│ │ ├── release.yml # GitHub Releases on tag push
│ │ └── lint-commits.yml # Conventional Commits enforcement
│ ├── semgrep/
│ │ └── ai-security.yml # 14 custom AI/security rules
│ └── scripts/
│ └── ai_security_check.py # 9 project-specific security checks
├── CHANGELOG.md
├── VERSION
└── docker-compose.yml
GitHub Actions runs on every push and PR:
| Workflow | Jobs | What it checks |
|---|---|---|
security.yml |
secrets-scan, sast-python, ai-security, docker-lint, shellcheck, deps-audit, trivy-images | Secret scanning, Python SAST, custom AI checks, container lint/scans |
release-please.yml |
release-please | Opens Release PR, updates CHANGELOG.md + VERSION |
release.yml |
github-release | Creates GitHub Release with notes on v*.*.* tag |
lint-commits.yml |
pr-title, commits, release-ready | Conventional Commits on PR + commit messages |
Custom Python checks (ai_security_check.py) cover 9 project-specific patterns:
ALLOWED_SKILLS check)add-user.sh.env.example.github/semgrep/ai-security.yml currently defines 14 Semgrep-style AI/security rules.
Current version: VERSION · CHANGELOG · Releases
CortexForge has several useful security experiments, but they are not a production security model:
QUOTA_KEY_* values instead of a direct upstream API keycorp-internal and corp-admin are internal Docker networks, while corp-outbound allows Telegram/external callshmac.compare_digestdeploy.resources/infra and has Docker socket accessUse docs/SECURITY.md as a threat-model note, not as a certification or production hardening guarantee.
docker compose config fails with "env file not found"instances/admin/.env is missing. Create it:
mkdir -p instances/admin
cp instances/.env.example instances/admin/.env
nano instances/admin/.env
make logs NAME=user-2
Common causes:
TELEGRAM_BOT_TOKEN — check with curl https://api.telegram.org/bot<token>/getMe.envVerify quota-proxy and CLIProxyAPI are on the corp-egress network:
docker inspect corp-quota | grep -A5 Networks
docker inspect corp-cliproxyapi | grep -A5 Networks
Both should include corp-egress. If CLIProxyAPI has no OAuth credential in its volume, complete the OAuth bootstrap first. If networks are wrong, run make deploy to recreate with the current docker-compose.yml.
make quota-report
Increase the limit without restart:
make set-limit NAME=user-2 LIMIT=2000000
The skills whitelist (_ALLOWED_SKILLS) is populated at startup from SKILLS_DIR. Restart the service-agent container after adding a new skill:
docker compose restart assistant-service
| Document | Description |
|---|---|
| docs/SETUP.md | Server installation (Ubuntu 24.04), systemd, cron backup |
| docs/ONBOARDING.md | Full employee onboarding checklist |
| docs/PERSONAS.md | Persona creation guide with examples (SOUL.md / IDENTITY.md) |
| docs/MESSAGING.md | Inter-instance messaging API reference |
| docs/SERVICE_AGENT.md | Service-agent HTTP API, adding skills, replication |
| docs/SECURITY.md | Threat model, 17 patched CVEs, CI/CD pipeline, hardening roadmap |
git checkout -b feat/your-featurefeat:, fix:, docs:, ci:, security:make security-check and python3 .github/scripts/ai_security_check.py locally before pushingmaster — CI will run all security checks automaticallyMIT — see LICENSE
CortexForge
03 / All projects
Libraries, command-line tools and applications.