Working as a team
Access in Contexo is per repo. You are a member of a repo or you are not; there are no per-page or per-directory permissions. A repo has exactly two roles.
| Role | Can |
|---|---|
owner |
everything a member can, plus mint / list / revoke invite keys and remove members |
member |
pull and push pages, read history, list members, read the activity feed |
You become an owner by creating the repo — from the dashboard, or implicitly the first time
ctx push targets a repo id that doesn’t exist yet. You become a member by redeeming an invite key.
There is no endpoint or command to change somebody’s role afterwards: the member routes are exactly
GET /v1/repos/:id/members and DELETE /v1/repos/:id/members/:userId, and nothing promotes an
existing member. owner is only ever written when a repo is created, or when a repo that has no
owner row is claimed — which is also how the very first user to sign in adopts repos created earlier
under a shared key.
Inviting someone
Section titled “Inviting someone”An owner mints a key and sends the token to the teammate:
ctx invite mint --label "sam laptop"The output is three lines: the ctxi_… token, then
expires 2026-08-04 12:00 UTC · label "sam laptop", then
share this with a teammate; they run 'ctx join <token>'. Keys live for seven days and can be
revoked before that.
The token is shown once, at mint time. The server stores only a hash, so ctx invite list can
never print it again — lose it and you mint another.
ctx invite list shows a repo’s keys:
no invite keysOnce keys exist, each row is <key-id> active|expired expires <date> UTC <label>. Two things to
know: the list includes expired keys — the active/expired word is computed by the CLI by
comparing the expiry to the current time, not filtered server-side — and ctx invite revoke takes
the short key id from the first column, never the ctxi_… token.
Joining
Section titled “Joining”The teammate needs three things before ctx join will work: a .contexo/ directory, credentials,
and a server URL.
ctx init # creates .contexo/, wires their agentsctx login # browser sign-in, mints a personal access tokenctx join ctxi_… # redeems the invitectx pull --full # fetch everything the team already knowsctx login fills the server URL in for them — it defaults to the hosted https://api.contexo.live
when nothing is configured yet. Against a self-hosted server, pass ctx login --server https://…
(or run ctx remote set first), otherwise ctx join stops with
join: server URL not set.
ctx join writes the resolved repo id into .contexo/config.json, so push and pull work
immediately afterwards. It prints Joined <repo-id> as member. 'ctx pull' to fetch existing pages.
Pulled 1 page(s); HEAD=a4b2c1d3Re-joining is idempotent — redeeming a key you’ve already used just confirms the membership you already have, and it does not consume a member slot. Nor does it demote an owner who redeems a key for their own repo: an existing role is preserved.
Their agents are wired by ctx init, not by the invite — see
connect your agent. At that point their agent can read the team’s
pages, and the daily flow applies to them too.
Seeing who has access
Section titled “Seeing who has access”no membersOnce people have joined, each row is the member’s email, their role, and joined <date>, ordered by
when they joined (oldest first). Any member may list members.
Removing somebody is owner-only:
ctx members remove sam@example.comThe CLI resolves that email against the member list first (case-insensitively) to get the user id, so the command only works for people you can already see. Three refusals are worth recognising:
only an owner can remove members— a 403 from the server; you holdmember.can't remove the last owner— a repo must always have at least one owner, and no route promotes a member, so a sole owner cannot hand the repo over and leave.not a member of this repo— the id resolved but the row was already gone.
Removal revokes access to future pulls and pushes. It does not rewrite history: commits that person
authored keep their name and email, which is what makes ctx history useful a year
later.
The activity feed
Section titled “The activity feed”ctx members tells you who can read the repo. ctx activity tells you who
actually did.
no activity yetno activity yetOnce events exist, each row is the actor’s email, pushed or pulled, and a UTC timestamp. What
gets recorded is narrower than you might assume:
- A push is recorded only when at least one file was actually committed. A push that 409s on every file records nothing.
- A pull is recorded only when pages were actually received. Routine “already up to date” polls — which agents make constantly — never reach the feed.
- Actions taken with a legacy shared API key are recorded not at all: there is no user id to attribute them to.
- The feed keeps the newest 1000 events per repo; older ones are pruned as new ones arrive.
--limitis capped at 500 per request by the server.
Agent attribution
Section titled “Agent attribution”Each event carries a detail blob that the CLI’s three-column output doesn’t print:
- push →
{"paths":["wiki/concepts/stripe-subscription.md", …]}, capped at 50 paths so one enormous push can’t bloat the row. - pull →
{"client":"claude-code"}, taken from theX-Contexo-Clientheader.
That client name is how you tell a human pull from an agent pull. The MCP server forwards whatever
the agent sent as clientInfo.name in its initialize request — claude-code, cursor, and so on
— while the CLI sends the literal ctx-cli. An agent that announces no client name produces a pull
event with no detail at all.
The detail is returned by GET /v1/repos/:id/activity. To see it from a terminal today, query that
endpoint directly rather than expecting ctx activity to show it.
Hosted plan limits
Section titled “Hosted plan limits”A self-hosted server applies no caps — the open-source build injects an unlimited quota policy,
so repo and member counts are unbounded. The hosted server at https://api.contexo.live injects a
plan policy instead. When a cap is hit, the API answers 402 Payment Required with a structured
body:
{ "error": "…upgrade prompt…", "code": "quota_members", "limit": 3, "upgrade_url": "https://contexo.live/#pricing"}code is quota_repos (creating one repo too many) or quota_members (one join too many). The cap
applies to the repo’s owners, not the joiner — a repo is uncapped if any of its owners is subscribed.
Re-joins never count against it.
Onboarding checklist
Section titled “Onboarding checklist”For the person joining:
- Install
ctxand runctx initin the project root. ctx login.ctx join <invite-key>.ctx pull --full.ctx status— confirm server, repo, authentication and page count.ctx mcp status— confirm their agents are wired to the knowledge base.
Agent integrations for this project:
AGENT MCP SERVER CAPTURE (STOP HOOK)
Claude Code wired (.mcp.json) installed
Cursor wired (.cursor/mcp.json) installed
Codex not installed not installed
Add another agent (Windsurf, OpenCode, Hermes, ...)? Run `ctx mcp guide`.See also
Section titled “See also”ctx invite,ctx join,ctx members,ctx activity— flag-level reference- The daily flow — what the team does once everyone is in
- Drift and conflicts — what happens when two members edit one page
