Initial curated itworx-knowledge-hub source from 3b1751d0d9dee551aa6994eee0093704dc334817
This commit is contained in:
commit
ad8ad252a9
825 files changed
+124216
No files matched your search
@@ -0,0 +1,240 @@
|
||||
# API contract
|
||||
|
||||
The API is built for the web client and trusted scoped automation. A future separate MCP
|
||||
server consumes the same API.
|
||||
|
||||
The draft OpenAPI document is in `docs/api/openapi.yaml`. Implementation and contract
|
||||
tests must keep it current.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Base path: `/api/v1`
|
||||
- JSON: UTF-8, camelCase
|
||||
- IDs: opaque UUID strings
|
||||
- Times: RFC 3339 UTC
|
||||
- Errors: `application/problem+json` (RFC 7807)
|
||||
- Pagination: opaque cursor
|
||||
- Concurrency: strong ETag plus `If-Match`
|
||||
- Correlation: accept/return `X-Correlation-ID`
|
||||
- Idempotency: `Idempotency-Key` for retryable create/append endpoints
|
||||
- Locale: server returns invariant values plus localized UI copy remains client-side
|
||||
|
||||
## Authentication
|
||||
|
||||
### Browser
|
||||
|
||||
Secure cookie session plus anti-forgery token for mutations.
|
||||
|
||||
### Service account
|
||||
|
||||
```http
|
||||
Authorization: Bearer ikh_<public-id>_<secret>
|
||||
```
|
||||
|
||||
The full token is shown once. The server stores:
|
||||
|
||||
- service account ID;
|
||||
- token public ID/prefix;
|
||||
- SHA-256 or keyed verifier of a cryptographically random 256-bit secret;
|
||||
- created/last-used/expiry/revoked timestamps;
|
||||
- scopes and optional allowed network policy.
|
||||
|
||||
For controlled curation, `pages:read` authorizes page detail, roots, children,
|
||||
revisions and backlinks; `pages:write` authorizes create, update, move and reversible
|
||||
trash. Browser callers retain their role-based cookie policy. Update and move accept an
|
||||
optional `Idempotency-Key` in addition to mandatory `If-Match`, so bounded automation
|
||||
can retry without duplicating a successful mutation.
|
||||
|
||||
The API never returns or logs the secret.
|
||||
|
||||
## Core scopes
|
||||
|
||||
```text
|
||||
knowledge:read
|
||||
knowledge:search
|
||||
pages:read
|
||||
pages:write
|
||||
projects:read
|
||||
projects:write
|
||||
project-updates:append
|
||||
decisions:read
|
||||
decisions:write
|
||||
runbooks:read
|
||||
attachments:read
|
||||
attachments:write
|
||||
imports:read
|
||||
imports:execute
|
||||
audit:read
|
||||
system:read
|
||||
```
|
||||
|
||||
Scopes never bypass workspace/content authorization.
|
||||
|
||||
## Resource endpoints
|
||||
|
||||
### System
|
||||
|
||||
- `GET /system/info`
|
||||
- `GET /health` is outside `/api/v1` and contains no private details
|
||||
|
||||
### Search
|
||||
|
||||
- `GET /search?q=&types=&tags=&projectId=&cursor=&limit=`
|
||||
|
||||
Returns permission-filtered hits and facets. No inaccessible counts or snippets.
|
||||
|
||||
### Pages
|
||||
|
||||
- `GET /pages/{id}`
|
||||
- `GET /pages/{id}/children`
|
||||
- `GET /pages/{id}/relations`
|
||||
- `GET /pages/{id}/revisions`
|
||||
- `GET /pages/{id}/revisions/{revision}`
|
||||
- `POST /pages`
|
||||
- `PUT /pages/{id}` with `If-Match`
|
||||
- `POST /pages/{id}/move` with `If-Match`
|
||||
- `POST /pages/{id}/trash` with `If-Match`
|
||||
- `POST /pages/{id}/restore`
|
||||
- `POST /pages/{id}/restore-revision`
|
||||
|
||||
Page responses include:
|
||||
|
||||
- stable ID;
|
||||
- current title/slug/breadcrumb;
|
||||
- raw Markdown when scope permits;
|
||||
- typed metadata;
|
||||
- current revision;
|
||||
- ETag;
|
||||
- stable source citation object.
|
||||
|
||||
### Projects
|
||||
|
||||
- `GET /projects`
|
||||
- `GET /projects/{id}`
|
||||
- `POST /projects`
|
||||
- `PUT /projects/{id}` with `If-Match`
|
||||
- `GET /projects/{id}/updates`
|
||||
- `POST /projects/{id}/updates`
|
||||
- `GET /projects/{id}/decisions`
|
||||
- `GET /projects/{id}/context`
|
||||
|
||||
### Project context
|
||||
|
||||
`GET /projects/{id}/context` returns a bounded context bundle:
|
||||
|
||||
```json
|
||||
{
|
||||
"project": {
|
||||
"id": "...",
|
||||
"name": "Ludarium",
|
||||
"status": "active",
|
||||
"summary": "...",
|
||||
"currentStateMarkdown": "...",
|
||||
"updatedAt": "..."
|
||||
},
|
||||
"recentUpdates": [],
|
||||
"activeMilestones": [],
|
||||
"decisions": [],
|
||||
"runbooks": [],
|
||||
"documents": [],
|
||||
"knownIssues": [],
|
||||
"citations": [],
|
||||
"truncation": {
|
||||
"truncated": false,
|
||||
"omittedCounts": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Requirements:
|
||||
|
||||
- default bounded item counts;
|
||||
- explicit `updatedAt` and source IDs;
|
||||
- deterministic ordering;
|
||||
- permission filtering before assembly;
|
||||
- maximum serialized size;
|
||||
- no secrets, raw attachment bodies or private filesystem paths;
|
||||
- truncation metadata rather than silent omission.
|
||||
|
||||
### RAG publication manifest
|
||||
|
||||
`GET /rag/publication-manifest?space=&cursor=&limit=` — a single, deliberately narrow
|
||||
read-only endpoint for RAGcore's n8n sync, reusing the existing `pages:read` scope (no
|
||||
new scope). There is no general `GET /pages` enumeration in this API — only
|
||||
`/pages/roots` plus a tree walk — so this is the one reliable way an external consumer
|
||||
discovers the full curated publication set. Selection and shape are entirely driven by
|
||||
the `rag:publish`/`rag:space:`/`rag:allow-deprecated`/`locale:` tag convention; see
|
||||
`docs/32-rag-publication-convention.md` for the full convention and
|
||||
`docs/api/openapi.yaml`'s `/rag/publication-manifest` path for the exact response shape.
|
||||
|
||||
### Attachments
|
||||
|
||||
The implemented v1 routes accept either the normal cookie role or the matching
|
||||
least-privilege service-account scope: `attachments:read` for listing/content and
|
||||
`attachments:write` for upload. `pages:write` also authorizes move and the reversible
|
||||
`POST /pages/{id}/trash` operation so an importer can roll back only the pages from
|
||||
its own recorded batch; it does not grant permanent purge access.
|
||||
|
||||
- `POST /attachments` streaming multipart
|
||||
- `GET /attachments/{id}/metadata`
|
||||
- `GET /attachments/{id}/content`
|
||||
- `DELETE /attachments/{id}` logical delete
|
||||
|
||||
Range requests may be supported after authorization. Unsafe types download as attachment.
|
||||
|
||||
### Imports
|
||||
|
||||
Administrative API only:
|
||||
|
||||
- create discovery/preflight;
|
||||
- retrieve plan/report;
|
||||
- execute accepted plan;
|
||||
- validate;
|
||||
- rollback.
|
||||
|
||||
Source paths are configured server-side; clients cannot supply arbitrary unrestricted
|
||||
host paths.
|
||||
|
||||
### Service accounts
|
||||
|
||||
Administrative browser/API endpoints create, list, rotate and revoke accounts/tokens.
|
||||
Creation/rotation returns the secret once.
|
||||
|
||||
## Error types
|
||||
|
||||
At minimum:
|
||||
|
||||
- validation error;
|
||||
- authentication required;
|
||||
- forbidden;
|
||||
- not found (without existence leakage);
|
||||
- concurrency conflict;
|
||||
- idempotency conflict;
|
||||
- rate limited;
|
||||
- unsupported media type;
|
||||
- payload too large;
|
||||
- import preflight conflict;
|
||||
- dependency unavailable.
|
||||
|
||||
Every problem includes a stable machine-readable `type` and correlation ID.
|
||||
|
||||
## Rate limits
|
||||
|
||||
Separate policies:
|
||||
|
||||
- login/recovery/bootstrap;
|
||||
- service-token authentication failures;
|
||||
- search;
|
||||
- attachment upload;
|
||||
- import/backup operations;
|
||||
- general API.
|
||||
|
||||
Limits are configurable and return standards-compatible retry guidance.
|
||||
|
||||
## Versioning policy
|
||||
|
||||
- Additive compatible fields may appear within v1.
|
||||
- Clients must ignore unknown fields.
|
||||
- Breaking changes require `/api/v2` or a documented transition.
|
||||
- Outbox event payloads carry their own schema version.
|
||||
- The future MCP server may never query the database directly.
|
||||
Reference in new issue
Block a user