Skip to content

feat(agent-connect): per-user API tokens + Team-gated /api/remember#11

Merged
Coding-Dev-Tools merged 1 commit into
mainfrom
feat/agent-connect
Jul 15, 2026
Merged

feat(agent-connect): per-user API tokens + Team-gated /api/remember#11
Coding-Dev-Tools merged 1 commit into
mainfrom
feat/agent-connect

Conversation

@Coding-Dev-Tools

Copy link
Copy Markdown
Owner

What

Team members connect their agents to a hosted Engraphis instance and store memories in the cloud v2 store instead of running Engraphis locally. A Team license (the instance's) is required to write — a free/lapsed instance returns 402.

This is the Phase 1 enabler from the team-hosting plan: the admin deploys one Railway instance (their account, their cost), activates a Team key, invites members; members sign in at https://team.engraphis.com/, mint a bearer token, and point their agent at /api/remember. No local install for teammates.

Changes

  • auth (inspector/auth.py): api_tokens table + create/resolve/list/revoke_api_token. Tokens are SHA-256 hashed at rest (raw token shown once); resolve rejects revoked + disabled-owner tokens.
  • routes/v2_team.py: POST /api/auth/token, GET /api/auth/tokens, DELETE /api/auth/token/{id}, GET /api/auth/connect-info (verify a token + discover the API base/snippet).
  • dashboard_app._auth_gate: accepts a per-user bearer token exactly like a cookie session (try bearer, then cookie), bound to the member for role + personal-folder authz. Instance ENGRAPHIS_API_TOKEN bypass unchanged.
  • routes/v2_api.py: POST /api/remember gated by _paid('team') (402 without a Team license); params mirror the local engraphis_remember MCP tool; writes hit the same v2 store the dashboard reads (no agent/UI split).
  • docs/AGENT_CONNECT.md + CHANGELOG [Unreleased].

Tests

tests/test_agent_connect.py — 7 tests: token lifecycle + connect-info, bearer write + recall, 402 without Team license, 401 without auth, disabled-user token rejected, instance-token bypass still works. Full suite: 701 passed, 3 skipped, 0 failed. Ruff clean.

How 'they need a Team license to connect' works

The Team license is the instance's (admin activates one key; the relay enforces the seat cap server-side). Members present a seat (account) + token, never a license. The 402 on /api/remember fires only when the instance has no active Team key — i.e. a free/lapsed instance can't host team agents. Login is never license-gated (so a lapsed key never locks the UI).

Deferred (follow-up PR)

MCP-over-HTTP at /mcp is intentionally NOT in this PR: mounting the MCP server as-is would spin up a second writer to the same SQLite (WAL lock contention — the exact issue mcp_server_http.py was written to avoid). It needs a service-injection refactor of mcp_server.py so it shares the dashboard's MemoryService, plus an MCP-client integration test. Agents use the HTTP API in the meantime.

Not in this PR

  • Frontend 'Connect your agent' / API-token management panel (the endpoints are ready; UI is a separate change to static/index.html).
  • Version bump / PyPI release (defer to a 0.9.6 release after review).
  • Railway one-click template + deploy button (Phase 2).

Team members connect agents to a hosted Engraphis instance and store memories
in the cloud v2 store instead of running locally.

- auth: per-user API tokens (api_tokens table) — SHA-256 hashed at rest, raw
  token returned once; resolve rejects revoked + disabled-owner tokens.
- v2_team: POST /api/auth/token, GET /api/auth/tokens, DELETE /api/auth/token/{id},
  GET /api/auth/connect-info (verify token + discover API base/snippet).
- dashboard_app._auth_gate: accept a per-user bearer token exactly like a cookie
  session (bound to the member for role + personal-folder authz); try bearer then
  cookie. Instance ENGRAPHIS_API_TOKEN bypass unchanged.
- v2_api: POST /api/remember gated by _paid('team') (402 without a Team license),
  params mirroring the local engraphis_remember MCP tool; writes hit the same v2
  store the dashboard reads.
- docs/AGENT_CONNECT.md; CHANGELOG [Unreleased].
- tests/test_agent_connect.py: 7 tests (token lifecycle, bearer write+recall,
  402 without Team license, 401 without auth, disabled-user token rejected,
  instance-token bypass). Suite: 701 passed, 3 skipped.

MCP-over-HTTP at /mcp is deferred (needs mcp_server service-injection to avoid a
second SQLite writer); agents use the HTTP API for now.
@Coding-Dev-Tools

Copy link
Copy Markdown
Owner Author

Council Gate Verdict — APPROVE_WITH_NITS

Risk level: high | Agreement: 0.746 | Council session: council-bcf4c8c8-c42b-4551-8897-7281b8083822

Per-model scores (rubric: correctness / safety / style / tests / complexity)

  • nvidia/nemotron-3-super-120b-a12b: REWORK (overall 4.2) — correctness=4.0, safety=3.0, style=5.0, tests=5.0, complexity=4.0
  • openrouter/meta-llama/llama-3.1-70b-instruct:free: APPROVE (overall 4.8) — correctness=5.0, safety=5.0, style=5.0, tests=5.0, complexity=4.0
  • openrouter/qwen/qwen3-coder:free: APPROVE (overall 4.2) — correctness=4.0, safety=5.0, style=4.0, tests=5.0, complexity=3.0
  • nvidia/llama-3.3-nemotron-super-49b-v1.5: APPROVE (overall 4.8) — correctness=5.0, safety=5.0, style=5.0, tests=5.0, complexity=4.0
  • oc/deepseek-v4-flash-free: APPROVE (overall 4.0) — correctness=4.0, safety=4.0, style=4.0, tests=4.0, complexity=4.0
  • openrouter/nvidia/nemotron-3-super-120b-a12b:free: REWORK (overall 2.2) — correctness=1.0, safety=2.0, style=1.0, tests=4.0, complexity=3.0
  • nvidia/nemotron-3-ultra-550b-a55b: APPROVE (overall 4.6) — correctness=5.0, safety=5.0, style=5.0, tests=5.0, complexity=3.0
  • oc/nemotron-3-ultra-free: APPROVE (overall 4.6) — correctness=5.0, safety=5.0, style=5.0, tests=5.0, complexity=3.0

Engraphis reference

Council verdict persisted as mem_01KXHKHC3RGT779N6CSBCT1T9E (workspace Coding-Dev-Tools). Also logged to security-council workspace as mem_01KXHKHC3YWAQMGS8CTB5GXS8C.

Posted automatically by the council-gate PR review cron. Auth/security PRs are reviewed at risk_level=high.

@Coding-Dev-Tools Coding-Dev-Tools added the council-approved Council gate approved this PR (APPROVE / APPROVE_WITH_NITS) label Jul 15, 2026
@Coding-Dev-Tools Coding-Dev-Tools merged commit 7e2048d into main Jul 15, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

council-approved Council gate approved this PR (APPROVE / APPROVE_WITH_NITS)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant