Files
yvgude--lean-ctx/docs/contracts/team-server-contract-v2.md
T
wehub-resource-sync 26382a7ac6
CI / Clippy (push) Failing after 15m13s
CI / Test (ubuntu-latest) (push) Failing after 16m1s
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Build (no embeddings / no ORT) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Cookbook (Node) (push) Has been cancelled
CI / Pi Extension (Node) (push) Has been cancelled
CI / Rust SDK (lean-ctx-client) (push) Has been cancelled
CI / Embed SDK (lean-ctx-sdk) (push) Has been cancelled
CI / Python SDK (leanctx) (push) Has been cancelled
CI / Hermes Plugin (Python) (push) Has been cancelled
CI / SDK Conformance Matrix (push) Has been cancelled
CI / Coverage (push) Has been cancelled
CI / cargo-deny (push) Has been cancelled
CI / Adversarial Safety (push) Has been cancelled
CI / Benchmarks (push) Has been cancelled
CI / Output-Quality Gate (eval A/B) (push) Has been cancelled
CI / Documentation (push) Has been cancelled
CI / CI Green (push) Has been cancelled
JetBrains Plugin / Actionlint (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
JetBrains Plugin / Validation (push) Has been cancelled
JetBrains Plugin / Build (push) Has been cancelled
JetBrains Plugin / Test (push) Has been cancelled
Security Check / Security Scan (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:30 +08:00

167 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Team Server Contract v2
GitLab: `#2331`, `#387`, `#388`
Pillar: Context Delivery
Scope: workspaces, scopes, audit log, dot-path rewrite, hosted-storage quota, ROI webhook, managed connectors
> v2 is **additive** over [v1](team-server-contract-v1.md): every v1 guarantee
> holds unchanged. v2 adds the optional `storageQuotaBytes` and `roiWebhookUrl`
> config keys (the v1 file is frozen; this file is the live, stable surface).
## Config (`TeamServerConfig`)
File is JSON.
- `host` (string, required)
- `port` (number, required)
- `defaultWorkspaceId` (string, required)
- `workspaces` (array, required; must include default workspace)
- `{ id, label?, root }`
- `tokens` (array, required for serve)
- `{ id, sha256Hex, scopes?, role? }`
- `sha256Hex` is lowercase hex SHA-256 of the plaintext token
- `scopes` and/or `role` (see [RBAC roles](#rbac-roles)); a token must yield at
least one effective scope
- `auditLogPath` (path, required)
- `disableHostCheck` (bool, default false)
- `allowedHosts` (string[], default [])
- `maxBodyBytes` (number, default 2097152)
- `maxConcurrency` (number, default 32)
- `maxRps` (number, default 50)
- `rateBurst` (number, default 100)
- `requestTimeoutMs` (number, default 30000)
- `statefulMode` (bool, default false)
- `jsonResponse` (bool, default true)
- `storageQuotaBytes` (number, optional, v2 / GL #387) — hosted-storage quota;
omitted ⇒ Team-tier 5 GiB default, `LEANCTX_TEAM_STORAGE_QUOTA_BYTES`
overrides both
- `roiWebhookUrl` (string, optional, v2 / GL #388) — https-only
Slack/Discord/generic webhook; when set, the server posts a weekly team-ROI
summary (once per ISO week, real reported numbers only; state in
`savings/roi_webhook_state.json`). A non-https URL is a startup error.
- `connectors` (array, optional, GL #281) — managed source syncs; each entry is a
`ConnectorConfig` (see [Managed Connectors](#managed-connectors-281)). Omitted ⇒
the feature is off and the scheduler never starts.
## Workspace selection
Workspace is selected deterministically via:
1. Header `x-leanctx-workspace` (if present and valid)
2. Otherwise `defaultWorkspaceId`
`POST /v1/tools/call` also accepts `workspaceId` in the JSON body (takes precedence over the header for that call).
## Dot-path rewrite (`rewrite_dot_paths`)
For arguments keys `path`, `target_directory`, `targetDirectory`:
- if value is `""` or `"."`, it is rewritten to the workspace root path before executing the tool.
## Scopes
Scope enforcement is tool/action-aware. Tokens must include required scopes for the requested tool.
| Scope | Grants Access To |
|-------|-----------------|
| `search` | `ctx_read`, `ctx_multi_read`, `ctx_smart_read`, `ctx_search`, `ctx_tree`, `ctx_outline`, `ctx_expand`, `ctx_delta`, `ctx_dedup`, `ctx_prefetch`, `ctx_preload`, `ctx_review`, `ctx_response`, `ctx_task`, `ctx_overview`, `ctx_pack`, `ctx_semantic_search`, `ctx_proof`, `ctx_verify` |
| `graph` | `ctx_graph`, `ctx_impact`, `ctx_callgraph`, `ctx_refactor`, `ctx_routes`, `ctx_pack` |
| `artifacts` | `ctx_artifacts`, `ctx_semantic_search` with `artifacts=true` |
| `index` | `ctx_graph` with `action=index-build*`, `ctx_semantic_search` with `action=reindex` |
| `events` | `GET /v1/events` SSE stream |
| `sessionMutations` | `ctx_session` (mutating: `save`, `set_task`, `task`, `checkpoint`, `finding`, `decision`, `reset`, `import`), `ctx_handoff`, `ctx_workflow`, `ctx_share` |
| `knowledge` | `ctx_knowledge` (mutating: `remember`, `feedback`, `remove`, `consolidate`), `ctx_knowledge_relations` (mutating: `relate`, `unrelate`) |
| `audit` | `GET /v1/metrics`, `GET /v1/connectors`, full-payload event access, audit log reads |
Errors:
- `401 unauthorized` (missing/invalid token)
- `403 scope_denied` (token lacks required scopes)
- `400 unknown_workspace`
## RBAC roles
A token's effective scopes are `scopes role.scopes()`. Roles (EPIC 13.2) are an
ergonomic layer over scopes — assign a coarse role instead of hand-picking
scopes. Enforcement is unchanged (the middleware evaluates effective scopes).
| Role | Effective scopes |
|------|------------------|
| `viewer` | `search` |
| `member` | `search`, `graph`, `index`, `knowledge`, `events` |
| `admin` | all scopes |
| `owner` | all scopes (org/billing authority is a hosted control-plane concern) |
Roles are monotonic: `viewer ⊆ member ⊆ admin = owner` (server scopes). Create
with `lean-ctx team token create --role <viewer|member|admin|owner>` (or
`--scopes <csv>`, or both).
> Additive / Team-Cloud plane only — never gates the local plane. SSO/SCIM,
> org-shared knowledge graph, and audit-retention dashboards build on this role
> model and are tracked on the commercial plane (EPIC 13.2).
## Managed Connectors (#281)
A *connector* is a scheduled, in-process sync from an external source into a
workspace's long-term stores (BM25 + graph + knowledge). Once a connector has
run, `ctx_semantic_search` and `ctx_knowledge` surface the source's issues / PRs
/ pipelines to every seat — no per-call credential transport, no manual
`ctx_provider` invocation.
### `ConnectorConfig`
Each entry of `connectors[]` (camelCase JSON):
- `id` (string, required) — stable, file-safe, unique within the instance.
- `provider` (string, required) — `gitlab` | `github`.
- `resource` (string, required) — gitlab `issues|merge_requests|pipelines`;
github `issues|pull_requests|actions`.
- `project` (string, optional) — `group/project` (GitLab) or `owner/repo` (GitHub).
- `host` (string, optional) — GitLab host (default `gitlab.com`) or GitHub API
base (default `https://api.github.com`).
- `state` (string, optional) — provider state filter (e.g. `opened`).
- `limit` (number, optional, default 50) — max items per sync.
- `intervalSecs` (number, optional, default 3600) — sync cadence, **clamped to a
300 s floor** to protect external APIs.
- `secret` (string, optional) — the provider credential. **Plaintext only inside
the private `team.json`** (a control-plane-injected env var); it is never
written to disk by the server and never returned by any endpoint.
- `workspaceId` (string, optional) — target workspace; the instance default when omitted.
- `displayName` (string, optional).
- `enabled` (bool, default true).
### Scheduler
A single background scheduler ticks once a minute, runs each *due* connector
(`now ≥ lastRun + effectiveInterval`) on the blocking pool, and records the
outcome per connector under `<state_dir>/<id>.json` (never the credential). When
the hosted index is **over quota** (GL #282) ingestion pauses (never deletes,
never gates reads); the connector records an `error` status and waits its
interval before retrying.
### `GET /v1/connectors`
Audit-scoped. Returns the **secret-free** roster + per-connector sync status
(`lastRunAt`, `lastStatus` `ok|error`, `lastError`, `lastItemCount`,
`totalRuns`, `totalItems`, `hasSecret`). The credential is never exposed.
## Audit log (JSONL)
Audit log is JSONL; one object per line:
- `ts` (RFC3339)
- `tokenId`
- `workspaceId`
- `tool`
- `method`
- `allowed` (bool)
- `deniedReason` (string|null)
- `argumentsMd5` (string; MD5 of canonicalized arguments JSON)
Raw arguments are never stored in the audit log.
## Implementation
- `rust/src/http_server/team.rs`
- CLI dispatch: `rust/src/cli/dispatch.rs` (`lean-ctx team ...`)