diff --git a/README.md b/README.md
index 358e7a0..ac84644 100644
--- a/README.md
+++ b/README.md
@@ -1,53 +1,25 @@
-# @context - professional context manager
+# @context — your work context, self-hosted
-@context is a self-hosted context manager. It organizes your work context into a private CRM and knowledge base and helps you stay on top of things.
+@context is a personal work-context proxy: a private CRM and knowledge base with an agent in front of it. Tell it things in plain language (*"met Kyle from Agno, wants a partnership, follow up next week"*) and it files contacts, notes, reminders, and specs for you — then briefs you back on demand (*"what's on today?"*). It plugs into Claude, Claude Code, Codex, ChatGPT, and Cursor over MCP, giving all your AI tools one shared source of truth about your work.
-It plugs into clients like claude, chatGPT, claude code, and codex, and gives them a single source of @context about your work. I use it with claude code to manage product specs.
+It's built for individuals who run their day across email, Slack, and a pile of AI tools — with privacy as a first principle: everything runs in your own infrastructure, and every byte lives in your own database.
-> Your AI tools are users of context, not competitors.
+> Built on [Agno](https://docs.agno.com)'s AgentOS runtime.
-@context is built with privacy and security as first principles. It runs in two modes:
+## How it works
-1. **Owner mode.** You get every tool. Capture context (*"met Kyle from Agno, follow up next week"*), retrieve context (*"give me a rundown of my day"*), and prepare context (*"process today"*).
-2. **Guest mode.** Teammates (*and their agents*) can leave updates in your queue. You get briefed when you ask for a rundown.
+| Piece | What it does |
+|---|---|
+| **One agent, role-gated tools** ([`agents/context.py`](agents/context.py)) | AgentOS verifies the caller on every request (JWT or Slack identity). The **owner** gets every tool; **guests** get capture-only — they can leave updates in your queue but never read or act. |
+| **CRM** (`crm` Postgres schema) | Structured memory: contacts, projects, meetings, reminders, notes. No forms — @context maps what you say onto the right table. See [`docs/CRM.md`](docs/CRM.md). |
+| **Knowledge base** (`knowledge/`, or a Git repo) | Specs, research notes, briefs. Filesystem-backed by default; back it with a private Git repo for production. See [`docs/KNOWLEDGE.md`](docs/KNOWLEDGE.md). |
+| **Skills** ([`skills/`](skills/)) | Owner playbooks invoked in natural language: daily rundown, week plan, meeting prep, process-today, research, knowledge review. |
+| **Workflows + schedules** ([`workflows/`](workflows/), [`app/schedules.py`](app/schedules.py)) | An hourly reminder sweep files due reminders into your queue; daily/weekly digests DM you on Slack when Slack is configured. |
+| **External actions, double-gated** | Calendar changes pause for explicit approval (`requires_confirmation`); Gmail can *only* draft — it never sends. See [`docs/SECURITY.md`](docs/SECURITY.md). |
-@context runs on Agno's AgentOS runtime, so user identity is verified on every request, and tools are assigned by role (owner or guest).
+Guests (teammates and *their* agents) can @-mention your @context on Slack to drop updates in your queue — and your @context can do the same to theirs. That's the [context network](docs/NETWORK.md).
-> Built on [Agno](https://docs.agno.com).
-
-## Job Scope
-
-@context has five jobs.
-
-1. **Maintain a CRM.** Share *"met Kyle from Agno, wants a partnership, follow up next week"*, and it stores a contact, a note, and a dated reminder without you picking forms or fields.
-2. **Maintain a knowledge base.** @context writes product specs, parses customer interview notes, manages project briefs, and runs deep research, then keeps it all neatly organized in one place.
-3. **Run your day, plan your week, prep ahead.** @context runs repeatable playbooks to make things easier. A few come built in, and you should customize them and add your own. Here are the included ones:
- - **Rundown** *("what's on today?")*: a prioritized brief of things on your plate. One digest instead of five apps: the updates teammates (and their agents) left in your queue, reminders that are due, today's meetings, the emails you missed, and the Slack threads worth a look.
- - **Week plan** *("what's my week?")*: priorities for the week. Runs Sunday evening and lands in your DMs, so you start the week with 🔥
- - **Prep** *("prep for my 2pm with Kyle")*: a tight pre-meeting brief covering who they are, notes, past threads, what's still open, email and Slack exchanges, and public background from the web for contacts you don't know yet.
-
- @context runs these playbooks on demand or on a schedule. The daily rundown and weekly plan DM the brief straight to you.
-4. **Represent you.** Your teammates (and their agents) can share non-urgent updates with your @context. A teammate types *"@your-context my claude fixed the auth bug"*, and it lands in your queue and surfaces in your next rundown. It works outbound too. Your @context can message people and channels on Slack on your behalf, and @-mention a teammate's @context to drop an update in *their* queue. That's how a team's contexts talk to each other (the [context network](docs/NETWORK.md)). This keeps your signal-to-noise high.
-5. **Draft and schedule.** Connect [Gmail and Calendar](docs/GOOGLE.md), and @context reads your real inbox and calendar, drafts your follow-ups straight into Gmail for you to send, and sends calendar changes to your approvals queue.
-
-## Security
-
-@context is an alter ego with access to a lot of sensitive information, so the security boundaries need to be AIRTIGHT.
-
-Agno's AgentOS makes two things possible:
-
-1. **Verify the user making the request.** AgentOS extracts the `user_id` from the JWT or the Slack request, so we can tell whether the caller is the owner or a guest.
-2. **Assign tools by role.** Based on that, we add the right tools to the agent.
-
-This model lets us build a system that anyone can write to, but only you can read from or act through. To everyone else, it is a polite notetaker that only captures. Although it does remember who it is talking to: each caller gets their own user-memory, kept entirely separate from yours.
-
-External actions are double gated. Changing your calendar (`update_calendar`) pauses for explicit approval before it runs, using AgentOS's `requires_confirmation` and `approval_type="required"` settings. Email goes a step further. `update_gmail` can *only* draft, so the follow-up waits in your Gmail drafts for you to review and send. It never sends on its own.
-
-Everything runs locally or in your own cloud, inside your VPC. Every byte of data lives in your own database: the CRM, the knowledge base, and the inbox.
-
-Read [`docs/SECURITY.md`](docs/SECURITY.md) for more details.
-
-## Get started
+## Quick start
> Requires [Docker](https://www.docker.com/get-started/) installed and running.
@@ -55,336 +27,81 @@ Read [`docs/SECURITY.md`](docs/SECURITY.md) for more details.
git clone https://github.com/agno-agi/context.git
cd context
-# Configure credentials
cp example.env .env
-# Open .env: set OPENAI_API_KEY, and set OWNER_ID to the email you sign in to os.agno.com with.
-# OWNER_NAME is an optional display name, set it as your name.
+# In .env: set OPENAI_API_KEY, and set OWNER_ID to the email you sign in to os.agno.com with.
-# Run on Docker
docker compose up -d --build
```
-Confirm it is live at [http://localhost:8000/docs](http://localhost:8000/docs).
-
-## AgentOS UI
-
-@context runs on AgentOS, which comes with a web UI for managing and monitoring @context. Use the AgentOS UI to chat with @context, view sessions, approve actions and more.
-
-
-
-1. Open [os.agno.com](https://os.agno.com) and sign in with your email (the same one you set as `OWNER_ID`).
-2. Click **Connect AgentOS → Local**.
-3. Enter `http://localhost:8000`, name it "Local Context" and connect.
-4. Click on the chat button under Context.
-5. Try one of the quick prompts.
-
-## MCP server
-
-The main way to use @context is from an MCP client like Claude Code, Codex, Claude, Cursor, and ChatGPT. Connect your favorite AI tools to @context's MCP server at `http://localhost:8000/mcp`.
-
-> Note: @context's MCP server is **owner-only**, so keep an eye on security.
-
-### Add @context to MCP clients automatically
-
-Add @context to every MCP client on your machine with one command:
-
-```sh
-python scripts/connect.py
-```
-
-The script finds Claude Code, Codex, the Claude Desktop app, and Cursor, and registers @context with each. Use `--dry-run` to preview and `--remove` to undo. Once you've deployed, the same script points your clients at the live instance — see [Connect production @context MCP server](#connect-production-context-mcp-server).
-
-### Add @context to MCP clients manually
-
-For CLI clients, run the following commands:
-
-```sh
-claude mcp add -s user --transport http context http://localhost:8000/mcp # Claude Code (user scope)
-
-codex mcp add --url http://localhost:8000/mcp context # Codex
-```
-
-**Claude Desktop** needs that bridge in `claude_desktop_config.json`, because its "Add custom connector" dialog only accepts `https` URLs. Add this (keep any existing keys) and restart the app:
-
-```json
-{
- "mcpServers": {
- "context": {
- "command": "npx",
- "args": ["-y", "mcp-remote", "http://localhost:8000/mcp", "--transport", "http-only"]
- }
- }
-}
-```
-
-**Cursor** speaks remote MCP directly (no bridge). Add this to `~/.cursor/mcp.json` and restart Cursor:
-
-```json
-{
- "mcpServers": {
- "context": {
- "url": "http://localhost:8000/mcp"
- }
- }
-}
-```
-
-See [`docs/MCP.md`](docs/MCP.md) for more details.
-
-## Slack
-
-Slack is where @context comes alive. It's the interface where I (@ashpreetbedi) use it the most and the interface that allows your team (and their agents) to talk to @context.
-
-To set it up, you need to:
-1. Create a Slack app
-2. Get the Bot User OAuth Token and Signing Secret
-3. Set the environment variables in `.env` or `.env.production`
-4. Restart the application
+Confirm it's live at [http://localhost:8000/docs](http://localhost:8000/docs). Compose runs two containers: `context-api` (FastAPI + AgentOS, hot-reload in dev mode) and `context-db` (Postgres with pgvector).
-Read [`docs/SLACK.md`](docs/SLACK.md) for the Slack setup guide.
+## Interfaces
-## @context Knowledge Base
+**AgentOS web UI.** Open [os.agno.com](https://os.agno.com), sign in with the email you set as `OWNER_ID`, click **Connect AgentOS → Local**, and enter `http://localhost:8000`. Chat with @context, view sessions, and approve actions from there.
-@context comes with a knowledge base that acts as its second brain. @context stores everything from product specs and research notes to "what I know about X" pages in this knowledge base.
-
-The knowledge base is stored on the filesystem by default (a gitignored `knowledge/` folder in this repo) but I highly recommend pointing it to a git repo or notion database for production. See [`docs/KNOWLEDGE.md`](docs/KNOWLEDGE.md) for the full guide.
-
-Try:
-- *Write a one-pager on the advantages of building our own agent-platform*
-- *Write up a decision: we're standardizing on agno*
-- *What in my knowledge base needs attention?*
-
-## @context CRM
-
-@context comes with a CRM that gives it structured memory about people, projects, meetings, reminders, notes and contacts.
-
-This auto-managing crm is @context's superpower. Use it to manage projects, meetings, reminders, notes, and contacts. @context maps what you tell it onto the right table - no forms, no fields - and can create new tables on demand. Try:
-- *"Add Dana Reyes, Head of Platform at Acme, dana@acme.com - and remind me to send her the integration spec next Tuesday."*
-- *"Who do I know at Acme?"*
-- *"What reminders do I have coming up?"*
-- *"Tell me about Northwind."*
-
-@context's database lives in the `crm` Postgres schema: writes are confined to that schema and every row is scoped to your `user_id`, so a guest can't see this data. See [`docs/CRM.md`](docs/CRM.md) for the schema, the filing rules, and the write boundary.
-
-## Run in production
-
-@context runs anywhere that runs a Docker container.
-
-For a quick deployment, the repo includes a script to run on Railway. The `scripts/railway/up.sh` script will run @context as a service with Postgres on the same private network. It reads credentials from `.env.production`, and creates a public domain you connect to in the AgentOS UI.
-
-> Requires the [Railway CLI](https://docs.railway.com/cli#installing-the-cli)
-
-### 1. Production env
-
-Create the production environment file.
+**MCP (Claude Code, Codex, Claude Desktop, Cursor, ChatGPT).** @context ships AgentOS's built-in MCP server at `http://localhost:8000/mcp`, exposing one owner-only tool: `use_context`. Wire up every MCP client on your machine with one command:
```sh
-cp .env .env.production
+python scripts/connect.py # --dry-run to preview, --remove to undo
```
-The deploy scripts read `.env.production` first and fall back to `.env` if it doesn't exist.
+Once deployed, `./scripts/setup_context.sh` mints a self-issued JWT and points your clients at the live instance. Per-client details in [`docs/MCP.md`](docs/MCP.md).
-### 2. Deploy
+**Slack.** The interface that lets your team (and their agents) talk to @context. Set `SLACK_BOT_TOKEN` and `SLACK_SIGNING_SECRET` and restart; setting the bot token also arms the scheduled daily/weekly digest DMs. Setup guide: [`docs/SLACK.md`](docs/SLACK.md).
-Run the `up.sh` script to run @context + postgres on Railway.
+## Deploy
-First, login to Railway.
+@context runs anywhere that runs a Docker container. For Railway (the included path — requires the [Railway CLI](https://docs.railway.com/cli#installing-the-cli)):
```sh
+cp .env .env.production # deploy scripts read .env.production, falling back to .env
railway login
+./scripts/railway/up.sh # @context + Postgres on a private network, public domain for the API
```
-Then run the `up.sh` script.
-
-```sh
-./scripts/railway/up.sh
-```
-
-The script will now pause and wait for you to mint the JWT verification key.
-
-### 3. Enable Token Based Authorization
+Two things to know:
-Token-Based Authorization is on by default. Without the `JWT_VERIFICATION_KEY` environment variable in `.env.production`, the AgentOS will not serve traffic. That is the safe default for an agent that has access to sensitive information. You can issue and verify your own JWT (see [BYO JWT](https://docs.agno.com/agent-os/security/authorization/self-hosted)) or mint a JWT verification key at [os.agno.com](https://os.agno.com).
+- **JWT is required at prod boot.** With `RUNTIME_ENV=prd`, AgentOS refuses to serve traffic without `JWT_VERIFICATION_KEY` — the safe default for an agent holding your inbox and CRM. `up.sh` pauses while you mint the key at [os.agno.com](https://os.agno.com) (**Connect AgentOS → Live** → enable **Token Based Authorization**, paste the public key into `.env.production`) or [bring your own JWT](https://docs.agno.com/agent-os/security/authorization/self-hosted).
+- **Single-replica scheduler.** The deploy ships `numReplicas: 1` ([`railway.json`](railway.json)); the built-in scheduler assumes it. Before scaling out, read [`docs/SCALING.md`](docs/SCALING.md) and pin `INTERNAL_SERVICE_TOKEN`.
-The `up.sh` script pauses and waits for you to add the JWT verification key to `.env.production`. Here's how you can get one from os.agno.com:
+Afterwards: `railway logs --service agent-os` to watch it come up, `./scripts/railway/env-sync.sh` to push `.env.production` changes, `./scripts/railway/redeploy.sh` after code changes, and `./scripts/setup_context.sh` to wire your MCP clients to production. If Slack was set up locally, repoint its request URLs to the Railway domain ([`docs/SLACK.md`](docs/SLACK.md#moving-from-local-to-production)).
-1. Open [os.agno.com](https://os.agno.com), click **Connect AgentOS → Live**
-2. The `up.sh` script will print the AgentOS domain, paste it into the input field.
-3. Enable **Token Based Authorization** and click **Connect**.
-4. Copy the public key and paste it into `.env.production`.
-5. Back in the terminal, press Enter. `up.sh` will read the key and deploy the AgentOS service to Railway.
+## Configuration
-### 4. Verify
-
-You can verify the deployment on the Railway dashboard or in the terminal by watching the logs:
-
-```sh
-railway logs --service agent-os
-```
+Copy [`example.env`](example.env) to `.env` — it documents every knob. The ones that matter most:
-If you add/update any values in `.env.production`, you can sync them to Railway with:
+| Variable | Required | Description |
+|---|---|---|
+| `OPENAI_API_KEY` | yes | Models and embeddings. |
+| `OWNER_ID` | prod | Comma-separated identities that count as the owner (JWT `sub` and/or Slack email). First is canonical. Unset means capture-only for everyone. |
+| `OWNER_NAME` / `OWNER_TIMEZONE` | no | Display name; IANA timezone anchoring "today" and due-date math (default UTC). |
+| `RUNTIME_ENV` | no | `dev` enables hot-reload and disables JWT (compose sets this); default `prd`. |
+| `JWT_VERIFICATION_KEY` | prod | Public key from os.agno.com. AgentOS won't serve traffic in `prd` without it. |
+| `SLACK_BOT_TOKEN` / `SLACK_SIGNING_SECRET` | no | Enable the Slack interface; the bot token alone activates the `slack` source and arms the digests. |
+| `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` / `GOOGLE_PROJECT_ID` | no | Gmail + Calendar; mint tokens with `python scripts/google_mint_tokens.py` ([`docs/GOOGLE.md`](docs/GOOGLE.md)). |
+| `KNOWLEDGE_REPO_URL` / `KNOWLEDGE_GITHUB_TOKEN` | no | Back the knowledge base with a Git repo instead of local files. |
+| `PARALLEL_API_KEY` | no | Authenticated web search (higher rate ceiling); recommended for production. |
+| `DAILY_DIGEST_CRON` / `WEEKLY_DIGEST_CRON` | no | UTC crons for the Slack digests (defaults `0 13 * * *` / `0 22 * * 0`). |
+| `DB_HOST` / `DB_PORT` / `DB_USER` / `DB_PASS` / `DB_DATABASE` | no | Postgres connection; defaults match compose. |
-```sh
-./scripts/railway/env-sync.sh
-```
+Never commit `.env` or `.env.production` — both are gitignored and carry real keys.
-### 5. Redeploy after code changes
+## Evals
-If you make code changes (which you most definitely will), you can redeploy the AgentOS service to Railway with:
+The eval suite ([`evals/`](evals/)) regression-tests the security claim: *anyone can write, only you can read.* It needs a local venv:
```sh
-./scripts/railway/redeploy.sh
-```
-
-Or enable auto-deploy in the Railway dashboard:
-
-1. Open the Railway dashboard
-2. Navigate to the agent-os service
-3. Click **Settings**
-4. Click **Source** and select the git repo for this project
-5. Set the deploy branch to `main` and click **Deploy**
+./scripts/venv_setup.sh && source .venv/bin/activate
-### 6. Point Slack at production
-
-If you set Slack up locally, your Slack app's request URLs still point at your local @context via the ngrok tunnel.
-
-Repoint the `/slack/events` and `/slack/interactions` request URLs to your Railway domain. AgentOS must already be deployed and serving traffic so Slack's URL re-verification passes.
-
-See [`docs/SLACK.md`](docs/SLACK.md#moving-from-local-to-production) for full steps.
-
-## Connect production @context MCP server
-
-Once @context is deployed (you have a Railway domain), point your MCP clients at the production endpoint instead of localhost. The deployed server is JWT-gated, so this needs a bearer token — for the MCP server, we mint our own and push it to Railway. One command does the whole thing:
-
-```sh
-source .venv/bin/activate # mint needs pyjwt + cryptography (in requirements)
-./scripts/setup_context.sh # login → mint token → push public key → redeploy → wire clients
+python -m evals # full suite
+python -m evals -v # stream the agent runs
+python -m evals --case # one case
```
-[`scripts/setup_context.sh`](scripts/setup_context.sh) is the single front door. By default it does a full setup: checks `railway login`, mints a fresh token, pushes the public key, runs a `railway up` redeploy (so a `railway.json` change like `numReplicas` lands), wires Claude Code, Codex, Claude Desktop, and Cursor with the token, then tells you to restart your apps. It never restarts apps for you or touches your data. Re-run it any time to rotate the token — add **`--no-redeploy`** to skip the redeploy and just rotate the token + rewire clients.
-
-Under the hood it chains three pieces you can also run by hand:
-
-1. [`scripts/mint_mcp_jwt.py`](scripts/mint_mcp_jwt.py) — self-issues an RS256 keypair (private key stays local in gitignored `secrets/`) and writes the public key + a signed admin token to `.env.production`.
-2. [`scripts/railway/env-sync.sh`](scripts/railway/env-sync.sh) — pushes the **public** key to Railway so the deploy trusts your token (the token itself stays off the server).
-3. [`scripts/connect.py --production`](scripts/connect.py) — threads the token into Claude Code, Codex, Claude Desktop, and Cursor.
-
-You self-issue the token instead of copying one from os.agno.com, and @context trusts your key *alongside* the os.agno.com one — so the [AgentOS UI](#agentos-ui) keeps working too.
-
-See [`docs/MCP.md`](docs/MCP.md#self-issued-production-token) for the full details: where the token comes from, per-client specifics (Codex's `$CONTEXT_JWT`, switching local→prod), ChatGPT/Claude web, and how it's secured.
-
-## Connect @context knowledge base to Git
+Exit 0 on all-pass, non-zero otherwise.
-When testing locally we can use the local `knowledge/` folder to store the knowledge base. But in production we need to back the knowledge base with a durable solution like a Git repo.
-
-Here's how to connect @context's knowledge base to a Git repo.
-
-1. Open [github.com/new](https://github.com/new) and create a new repo for your @context's knowledge base.
- - Name: `your-username/your-context`
- - Visibility: Mark the repo as private.
- - Add README: Yes
-2. Mint a GitHub token with push access to the repo.
- - Open [github.com/settings/personal-access-tokens](https://github.com/settings/personal-access-tokens) and create a new fine-grained token.
- - Click on **Generate new token**
- - Name: `Your Context`
- - Expiration: **No expiration**
- - Repository access: **Only select repositories** and select the repo you created in step 1.
- - **Add Permissions** -> **Select Contents**
- - Remember to update Access to **read and write**
- - Generate token and copy the token.
-3. Add both to `.env.production`:
- ```sh
- KNOWLEDGE_REPO_URL=https://github.com/you/your-specs.git
- KNOWLEDGE_GITHUB_TOKEN=ghp_...
- ```
-4. Sync to Railway:
- ```sh
- ./scripts/railway/env-sync.sh
- ```
-
-See [`docs/KNOWLEDGE.md`](docs/KNOWLEDGE.md) for the full guide.
-
-## Connect Gmail and Calendar
-
-You can connect your Gmail and Calendar to @context to ground the rundown and meeting prep in your real inbox and calendar.
-
-See [`docs/GOOGLE.md`](docs/GOOGLE.md) for more details.
-
-## Understanding the codebase
-
-@context has three main components. Review them in order.
-
-### The app (`app/`)
-
-@context is a FastAPI application running the AgentOS runtime. [`app/main.py`](app/main.py) is the entrypoint and [`app/settings.py`](app/settings.py) holds shared settings. [`app/identity.py`](app/identity.py) is where identity is validated. It looks dense, but all it does is check whether `user_id` is in the `OWNER_ID` list (comma-separated). [`app/mcp.py`](app/mcp.py) is the owner-only MCP server — one tool (`use_context`) that lets you read, act, and file through @context from the Claude/ChatGPT desktop apps and CLI clients (see [MCP server](#mcp-server)).
-
-### The agents (`agents/`)
-
-The main agent is [`agents/context.py`](agents/context.py). `context_tools()` adds tools to the agent based on the caller's role, and `caller_information()` adds the matching instructions.
-
-The supporting files:
-
-- [`agents/instructions.py`](agents/instructions.py) defines the role-specific instructions.
-- [`agents/sources.py`](agents/sources.py) defines the context providers (crm, knowledge, workspace, web, Slack, Gmail, Calendar) and how each registers its `query_` / `update_` tools.
-- [`agents/inbox.py`](agents/inbox.py) defines the inbound queue: `submit_update` (anyone), then `rundown` / `acknowledge` (you only).
-- [`agents/policy.py`](agents/policy.py) defines the pre-hook and tool-hook that back the owner/guest boundary.
-- [`workflows/`](workflows/) defines the runnable `Workflow` objects (the reminder sweep, the digests) and `dm_owner`; [`app/schedules.py`](app/schedules.py) registers their crons. The reminder sweep (`workflows/reminders.py`) files due reminders into the inbound queue, run hourly by the `queue-reminders` schedule.
-
-### The skills (`skills/`)
-
-The repo has **two distinct kinds of skill**. Keep them separate.
-
-- **Runtime skills** ([`skills/`](skills/)) are playbooks the deployed @context agent runs **for its owner**, invoked in natural language ("plan my week") and owner-gated. Add your own as needed.
-- **Coding-agent workflows** ([`.agents/skills/`](.agents/skills/)) are `/slash-command` workflows your *coding agent* (Claude Code, Codex, others) runs while **developing this repo**. They are covered under [Working with coding agents](AGENTS.md#working-with-coding-agents).
-
-Here are the runtime skills that are included in the repo:
-
-- [`skills/week-plan/SKILL.md`](skills/week-plan/SKILL.md).
-- [`skills/daily-rundown/SKILL.md`](skills/daily-rundown/SKILL.md).
-- [`skills/prep-for/SKILL.md`](skills/prep-for/SKILL.md).
-- [`skills/process-today/SKILL.md`](skills/process-today/SKILL.md).
-- [`skills/research/SKILL.md`](skills/research/SKILL.md).
-- [`skills/knowledge-review/SKILL.md`](skills/knowledge-review/SKILL.md).
-
-## Evals
-
-@context comes with an eval suite ([`evals/`](evals/)) for regression testing. It's tests the claim that: *anyone can write, only you can read.*
-
-Run it:
-
-```sh
-python -m evals # run the full suite
-python -m evals -v # stream the full agent run
-python -m evals --case # one case
-```
+## Source / links
-## Environment variables
-
-| Variable | Required | Default | Description |
-|----------|----------|---------|-------------|
-| `OPENAI_API_KEY` | yes | none | OpenAI key for models and embeddings. |
-| `OWNER_ID` | prd | none | Comma-separated identities that count as the owner (JWT `sub` and/or Slack email). First is canonical. Unset means capture-only for everyone. |
-| `OWNER_NAME` | no | canonical `OWNER_ID` | Display name rendered into the prompt. Cosmetic, never matched as an identity. |
-| `RUNTIME_ENV` | no | `prd` | `dev` enables hot-reload and disables JWT. Compose sets this to `dev` for local. |
-| `JWT_VERIFICATION_KEY` | prd | none | Public key from os.agno.com. Required when `RUNTIME_ENV=prd`. |
-| `CONTEXT_SELF_VERIFICATION_KEY` | no | none | A second JWT public key the app *also* trusts, alongside the os.agno.com key — your own, so tokens you self-issue with `scripts/mint_mcp_jwt.py` verify (the os.agno.com UI keeps working). Written by that script; pushed to the server by `env-sync.sh`. See [Connect production @context MCP server](#connect-production-context-mcp-server). |
-| `CONTEXT_MCP_JWT` | no | none | The self-issued bearer token `scripts/connect.py --production` threads into your MCP clients. Client-side only (lives in `.env.production`, never pushed to the server). Minted by `scripts/mint_mcp_jwt.py`. |
-| `AGENTOS_URL` | no | `http://127.0.0.1:8000` | Scheduler base URL. Also anchors the MCP server's Host allowlist — set it to your Railway/ngrok domain so the deployed or tunnelled `/mcp` endpoint accepts that Host (see [`docs/MCP.md`](docs/MCP.md)). |
-| `INTERNAL_SERVICE_TOKEN` | no | auto-generated | Scheduler-to-OS auth token. The deploy ships 1 replica, so the auto-generated value is fine; `scripts/railway/up.sh` still pins one so scaling up stays correct (override in `.env.production`). See [`docs/SCALING.md`](docs/SCALING.md). |
-| `PARALLEL_API_KEY` | no | none | Switches the `web` source from keyless Parallel MCP to the authenticated SDK (higher rate ceiling); recommended for production. Get a key at [platform.parallel.ai](https://platform.parallel.ai/settings?tab=api-keys). |
-| `SLACK_BOT_TOKEN` / `SLACK_SIGNING_SECRET` | no | none | Both enable the Slack interface. The bot token alone activates the `slack` source (`query_slack` + the ungated `update_slack` send tool) and auto-arms the scheduled digests. See [`docs/SLACK.md`](docs/SLACK.md). |
-| `DAILY_DIGEST_CRON` / `WEEKLY_DIGEST_CRON` | no | `0 13 * * *` / `0 22 * * 0` | UTC cron for the Slack-delivered daily rundown and weekly plan (only armed when Slack is set). See [`docs/SLACK.md`](docs/SLACK.md). |
-| `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` / `GOOGLE_PROJECT_ID` | no | none | Connect your Gmail + Calendar; mint tokens with `python scripts/google_mint_tokens.py`. See [`docs/GOOGLE.md`](docs/GOOGLE.md). |
-| `GMAIL_TOKEN_JSON_B64` / `CALENDAR_TOKEN_JSON_B64` | no | none | Minted Gmail/Calendar tokens as base64, so they survive a deploy. The entrypoint restores them at startup. See [`docs/GOOGLE.md`](docs/GOOGLE.md). |
-| `KNOWLEDGE_REPO_URL` / `KNOWLEDGE_GITHUB_TOKEN` | no | none | Set both to back the `knowledge` base with a Git repo instead of local files. Optional knobs: `KNOWLEDGE_BRANCH` (default `main`), `KNOWLEDGE_LOCAL_PATH`. |
-| `DB_HOST` / `DB_PORT` / `DB_USER` / `DB_PASS` / `DB_DATABASE` | no | matches compose | Postgres connection. |
-| `DB_DRIVER` | no | `postgresql+psycopg` | SQLAlchemy driver. |
-| `AGNO_DEBUG` | no | `False` | If `True`, Agno emits verbose debug logs. Compose sets this for dev. |
-| `WAIT_FOR_DB` | no | `False` | If `True`, the entrypoint blocks on the DB before starting. Compose sets this. |
-
-## Learn more
-
-- [`AGENTS.md`](AGENTS.md): architecture and conventions (source of truth for coding agents).
-- [Agno documentation](https://docs.agno.com)
-- [AgentOS introduction](https://docs.agno.com/agent-os/introduction)
-- [Agno on GitHub](https://github.com/agno-agi/agno) (drop a star if this is useful).
+- [`AGENTS.md`](AGENTS.md) — architecture and conventions (the source of truth for coding agents working on this repo).
+- [`docs/`](docs/) — deep dives: [CRM](docs/CRM.md), [knowledge](docs/KNOWLEDGE.md), [MCP](docs/MCP.md), [Slack](docs/SLACK.md), [Google](docs/GOOGLE.md), [security](docs/SECURITY.md), [scaling](docs/SCALING.md), [the context network](docs/NETWORK.md).
+- [Agno documentation](https://docs.agno.com) · [AgentOS introduction](https://docs.agno.com/agent-os/introduction) · [Agno on GitHub](https://github.com/agno-agi/agno) (drop a star if this is useful).
diff --git a/docs/MCP.md b/docs/MCP.md
index e36c0ab..8548e11 100644
--- a/docs/MCP.md
+++ b/docs/MCP.md
@@ -44,7 +44,7 @@ python scripts/connect.py --remove # undo
It detects Claude Code, Codex, the Claude Desktop app, and Cursor and wires @context into each — running `claude mcp add` / `codex mcp add` for the CLIs, writing an `mcp-remote` bridge into `claude_desktop_config.json` for the desktop app, and a native `{url, headers}` entry into `~/.cursor/mcp.json` for Cursor (absolute `npx` path resolved where a bridge is used, existing keys preserved, a timestamped backup made, anything already configured skipped). For Claude Code it also **always-allows** the `use_context` tool (adds `mcp__context__use_context` to `permissions.allow` in `~/.claude/settings.json`) so the agent never prompts you before calling it — see [Claude Code (CLI)](#claude-code-cli) below. Pure stdlib, so no venv needed. Useful flags: `--clients claude-code codex claude-desktop cursor` to limit the set, `--url` for a non-default endpoint, `--config-path` to point at a non-standard desktop config.
-**`--production`** targets your deployed instance: it reads `AGENTOS_URL` from `.env.production`, derives `https:///mcp`, and threads `Authorization: Bearer ` into every client for you. The JWT is read from `CONTEXT_MCP_JWT` in `.env.production`, else `--token `, else you're prompted — and you **self-issue** that token rather than copying one from os.agno.com (see [Self-issued production token](#self-issued-production-token) below). Claude Code gets the token via `--header`; Codex via `--bearer-token-env-var CONTEXT_JWT` (so it stays out of Codex's config — `export CONTEXT_JWT=` in your shell); Claude Desktop via the bridge's `--header`; Cursor via the `headers` block in `~/.cursor/mcp.json`. Switching a client from local to prod? CLI clients match by name, so re-run with `--force`. The full setup — minting the token, what lands where — is in [Self-issued production token](#self-issued-production-token) below; the [README](../README.md#connect-production-context-mcp-server) has the one-command quick-start.
+**`--production`** targets your deployed instance: it reads `AGENTOS_URL` from `.env.production`, derives `https:///mcp`, and threads `Authorization: Bearer ` into every client for you. The JWT is read from `CONTEXT_MCP_JWT` in `.env.production`, else `--token `, else you're prompted — and you **self-issue** that token rather than copying one from os.agno.com (see [Self-issued production token](#self-issued-production-token) below). Claude Code gets the token via `--header`; Codex via `--bearer-token-env-var CONTEXT_JWT` (so it stays out of Codex's config — `export CONTEXT_JWT=` in your shell); Claude Desktop via the bridge's `--header`; Cursor via the `headers` block in `~/.cursor/mcp.json`. Switching a client from local to prod? CLI clients match by name, so re-run with `--force`. The full setup — minting the token, what lands where — is in [Self-issued production token](#self-issued-production-token) below; the [README](../README.md#interfaces) has the one-command quick-start.
The per-client sections below are what it automates — reach for them to do it by hand, or to understand exactly what each form writes.
@@ -157,7 +157,7 @@ ngrok http 8000
The deployed server is JWT-gated, so every client needs a bearer token. You **self-issue** it rather than copying one from os.agno.com — which means the whole flow is scriptable and the token is durable (a config-file token you set once, not a short-lived browser session token).
-**Why self-issue.** AgentOS verifies a JWT against any public key it's configured to trust, and [`verification_keys` is a list](https://docs.agno.com/agent-os/security/authorization/self-hosted) — it tries each until one matches. The deployed app trusts **two** keys: the os.agno.com control-plane key (`JWT_VERIFICATION_KEY`, appended automatically by AgentOS, so the [AgentOS UI](../README.md#agentos-ui) keeps working) **and** a key you own (`CONTEXT_SELF_VERIFICATION_KEY`, wired in [`app/main.py`](../app/main.py)). Both issuers work at once. os.agno.com holds the private half of *its* key, so it mints the UI's tokens; you hold the private half of *yours*, so you mint your own MCP token.
+**Why self-issue.** AgentOS verifies a JWT against any public key it's configured to trust, and [`verification_keys` is a list](https://docs.agno.com/agent-os/security/authorization/self-hosted) — it tries each until one matches. The deployed app trusts **two** keys: the os.agno.com control-plane key (`JWT_VERIFICATION_KEY`, appended automatically by AgentOS, so the [AgentOS UI](../README.md#interfaces) keeps working) **and** a key you own (`CONTEXT_SELF_VERIFICATION_KEY`, wired in [`app/main.py`](../app/main.py)). Both issuers work at once. os.agno.com holds the private half of *its* key, so it mints the UI's tokens; you hold the private half of *yours*, so you mint your own MCP token.
**Mint it.** [`scripts/mint_mcp_jwt.py`](../scripts/mint_mcp_jwt.py) (run in the venv — it needs `pyjwt` + `cryptography`):
diff --git a/docs/SLACK.md b/docs/SLACK.md
index 3b0f767..8142fb5 100644
--- a/docs/SLACK.md
+++ b/docs/SLACK.md
@@ -11,7 +11,7 @@ The setup below also turns on two things beyond the chat interface: the `update_
## Prerequisites
-- @context running locally or in production (see [README#run-in-production](../README.md#run-in-production))
+- @context running locally or in production (see [README#deploy](../README.md#deploy))
- A Slack workspace where you can install @context
- [ngrok](https://ngrok.com/download) installed and running if you are running @context locally [not needed for production]
@@ -156,7 +156,7 @@ how are you?
If you first set @context up against a local ngrok URL, your Slack app is still pointed at your local AgentOS — events stop reaching it the moment ngrok closes. To switch the app to your deployed instance, repoint the two request URLs at your AgentOS (Railway) domain:
-1. Make sure @context is already deployed and serving in production (see [README#run-in-production](../README.md#run-in-production)).
+1. Make sure @context is already deployed and serving in production (see [README#deploy](../README.md#deploy)).
2. Confirm `SLACK_BOT_TOKEN`, `SLACK_SIGNING_SECRET`, and your `OWNER_ID` (with your Slack email) are in `.env.production`, then run `./scripts/railway/env-sync.sh` so the deployed instance has them.
3. Go to [api.slack.com/apps](https://api.slack.com/apps) → your app.
4. **Event Subscriptions** → set **Request URL** to `https:///slack/events` and wait for the green **Verified**.