diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md new file mode 100644 index 0000000..ce2a7e2 --- /dev/null +++ b/docs/implementation-plan.md @@ -0,0 +1,354 @@ +# gnexus-creds Implementation Plan + +## Stage 0: Scaffold + +Goal: create the initial project structure and runtime baseline. + +Deliverables: + +- `pyproject.toml` +- FastAPI application factory +- environment-based settings +- PostgreSQL connection setup +- Alembic migrations +- `/health` +- `/ready` +- JSON logging +- pytest setup +- dependency on `gnexus-auth-client-py` as a Python package + +Acceptance criteria: + +- app starts locally. +- `/health` works without database access. +- `/ready` checks database connectivity. +- migrations can be applied. +- test suite runs. +- `gnexus-auth-client-py` is installed as a dependency, not copied into the + repository. + +## Stage 1: Core Domain + +Goal: implement the domain model without external auth or MCP. + +Modules: + +```text +gnexus_creds/models/ +gnexus_creds/schemas/ +gnexus_creds/services/ +gnexus_creds/crypto/ +gnexus_creds/repositories/ +``` + +Initial database entities: + +- `users` +- `user_encryption_keys` +- `secrets` +- `secret_versions` +- `secret_fields` +- `secret_tags` +- `api_tokens` +- `audit_events` +- `sessions` +- `oauth_states` +- `rate_limits` + +Core behavior: + +- create user. +- create user encryption key on first user initialization. +- create secret. +- update metadata without creating a new version. +- update fields and create a new version. +- reveal current version. +- reveal historical version. +- hard delete secret while preserving audit events. +- search metadata, tags, field names, and non-encrypted field values. +- never search encrypted field values. + +Acceptance criteria: + +- core service tests cover versioning decisions. +- core service tests cover encryption/decryption. +- metadata update does not create a version. +- field update creates a version. +- old versions can be revealed. +- audit never stores secret values. + +## Stage 2: REST API + +Goal: expose the public JSON API under `/api/v1`. + +Endpoints: + +```text +GET /api/v1/me + +GET /api/v1/secrets +POST /api/v1/secrets +GET /api/v1/secrets/{id} +PATCH /api/v1/secrets/{id} +DELETE /api/v1/secrets/{id} + +POST /api/v1/secrets/{id}/reveal +GET /api/v1/secrets/{id}/versions +GET /api/v1/secrets/{id}/versions/{version_id} +POST /api/v1/secrets/{id}/versions/{version_id}/reveal + +GET /api/v1/categories +GET /api/v1/tags +GET /api/v1/suggestions + +GET /api/v1/audit-events +GET /api/v1/secrets/{id}/audit-events + +GET /api/v1/api-tokens +POST /api/v1/api-tokens +DELETE /api/v1/api-tokens/{id} + +POST /api/v1/export +POST /api/v1/import +DELETE /api/v1/account-data +``` + +API behavior: + +- JSON only. +- stable error envelope. +- offset/limit pagination. +- list/search returns metadata plus non-encrypted fields. +- reveal returns all fields. +- OpenAPI includes schemas, descriptions, and examples. +- API token auth uses token hash lookup. +- scopes: `read`, `reveal`, `write`, `admin`, `mcp`. + +Acceptance criteria: + +- REST integration tests cover CRUD. +- REST integration tests cover search/list/reveal. +- REST integration tests cover version history. +- REST integration tests cover API token scopes. +- REST integration tests cover audit creation. +- export/import round trip works. +- rate limiting protects sensitive endpoints. + +## Stage 3: gnexus-auth Integration + +Goal: implement real UI login and session flow. + +Deliverables: + +- dependency-backed `gnexus-auth-client-py` integration. +- OAuth login route. +- OAuth callback route. +- logout route. +- server-side sessions stored in PostgreSQL. +- OAuth state and PKCE storage in PostgreSQL. +- `/oauth/userinfo` user sync. +- first-login user initialization. +- `gnexus-auth` webhook endpoint. +- local disabled-user handling. +- role mapping for `user` and `admin`. + +Routes: + +```text +GET /auth/login +GET /auth/callback +POST /auth/logout +POST /webhooks/gnexus-auth +``` + +Acceptance criteria: + +- user can log in through `gnexus-auth.local`. +- local user is created on first login. +- user encryption key is created on first login. +- repeated login updates local profile fields. +- disabled users cannot log in. +- webhook updates profile/status. +- UI session cookie works. +- API token auth and UI session auth coexist. + +## Stage 4: MCP + +Goal: expose MCP over HTTP/SSE inside the same FastAPI application. + +Tools: + +```text +search_secrets +get_secret +reveal_secret +create_secret +update_secret +set_secret_status +archive_secret +``` + +Rules: + +- auth uses API token with `mcp` scope. +- tool actions additionally require `read`, `reveal`, or `write`. +- archived secrets are never returned. +- existing secret operations require `allow_mcp=true`. +- reveal creates audit event with `channel=mcp`. +- tool implementations reuse the REST service layer. + +Acceptance criteria: + +- MCP tests cover authentication. +- MCP tests cover scope checks. +- MCP tests cover `allow_mcp`. +- MCP tests cover archived secret exclusion. +- MCP tests cover reveal audit. +- HTTP/SSE transport works without stdio. + +## Stage 5: UI + +Goal: build the MVP frontend with Vue.js and `gnexus-ui-kit`. + +Dependency requirements: + +- `gnexus-ui-kit` is installed as a frontend dependency. +- no `gnexus-ui-kit` source code is copied into this repository. +- integration uses public Vue components/styles only. +- dependency version is lock-file pinned after successful integration. + +Screens: + +- login/session bootstrap +- dashboard/overview with search +- secrets list +- secret detail +- create/edit form +- reveal/copy controls +- version history screen +- audit list +- API token management +- export/import +- settings/delete data +- admin UI + +UI behavior: + +- default locale is English. +- locale is read from `gnexus-auth` when available. +- categories and tags use autocomplete. +- encrypted fields show name plus reveal/copy controls. +- reveal can display the value in-card. +- copy can work without visual reveal. +- dangerous actions use confirmation modals. +- core flows support keyboard navigation and visible focus states. + +Acceptance criteria: + +- all MVP user scenarios are available through UI. +- no direct dependency on private internals of `gnexus-ui-kit`. +- seed data supports manual end-to-end smoke testing. +- UI respects backend authorization failures. +- user role cannot access admin screens. + +## Stage 6: Admin + +Goal: provide role-based admin capabilities. + +Admin features: + +- list users. +- view user profile/status. +- view user-related system audit. +- view basic health/ready diagnostics. + +Constraints: + +- admin role is determined from `gnexus-auth`. +- backend enforces admin permissions. +- admins do not get decrypted user secrets by default. + +Acceptance criteria: + +- `user` role cannot access admin API routes. +- `user` role cannot access admin UI screens. +- `admin` role can access admin screens. +- admin API tests cover permission checks. + +## Stage 7: Packaging + +Goal: prepare the service for production deployment. + +Deliverables: + +- Dockerfile. +- production env example. +- migration command documentation. +- app start command documentation. +- README update. +- operational notes. + +Acceptance criteria: + +- container builds. +- app starts from container. +- app connects to PostgreSQL. +- migrations apply. +- production configuration is documented. + +## Testing Strategy + +Core: + +- service layer tests. +- crypto tests. +- versioning tests. +- audit safety tests. + +REST: + +- FastAPI integration tests. +- API token auth tests. +- scope tests. +- export/import tests. + +Auth: + +- mock `gnexus-auth` token/userinfo responses. +- webhook verification and parsing tests. +- session tests. + +MCP: + +- tool tests. +- auth/scope tests. +- access flag tests. + +UI: + +- no full e2e test suite in MVP. +- manual smoke testing with seed data. + +## Technical Choices + +Preferred defaults: + +- ORM: SQLAlchemy 2.x. +- migrations: Alembic. +- crypto: `cryptography`, AES-GCM with explicit key id, nonce, ciphertext, + and algorithm metadata. +- sessions: PostgreSQL-backed server-side sessions. +- API token format: `gcr__`, with only a hash stored. +- MCP library: choose during implementation after verifying FastAPI HTTP/SSE + compatibility. + +## Implementation Order + +1. Scaffold. +2. Core domain. +3. REST API. +4. `gnexus-auth` login/session integration. +5. MCP. +6. Vue UI with `gnexus-ui-kit`. +7. Admin UI/API. +8. Docker packaging and deploy docs. diff --git a/docs/technical-spec.md b/docs/technical-spec.md new file mode 100644 index 0000000..912d0d7 --- /dev/null +++ b/docs/technical-spec.md @@ -0,0 +1,469 @@ +# gnexus-creds Technical Specification + +## Overview + +`gnexus-creds` is a personal secret storage service with a REST API, MCP server, +and web UI. The service stores only personal user data. Team, organization, and +secret sharing features are out of scope for the MVP. + +The service is implemented in Python with FastAPI. The frontend is implemented +with Vue.js and `gnexus-ui-kit`. + +## External Dependencies + +`gnexus-auth-client-py` must be used as a backend dependency, not copied into +this repository. The integration must rely on its public API only. + +`gnexus-ui-kit` must be used as a frontend dependency, not copied into this +repository. The UI must rely on public exported Vue components/styles only. + +Both dependencies should be version-pinned through lock files while keeping a +clear upgrade path through the normal package manager flow. + +Expected dependency forms: + +```toml +gnexus-gauth @ git+https://git.gnexus.space/git/root/gnexus-auth-client-py.git +``` + +```json +{ + "gnexus-ui-kit": "git+https://git.gnexus.space/git/root/gnexus-ui-kit.git" +} +``` + +## Core Entity: Secret + +The main domain entity is `secret`. + +A secret has fixed metadata and a variable list of fields. Fixed metadata: + +- `title` +- `purpose` +- `category` +- `tags` +- `source` +- `notes` +- `status` +- `archived` +- access flags +- timestamps + +`notes` is limited to 140 characters. + +`status` has two values: + +- `actual` +- `outdated` + +`archived` hides a secret from ordinary lists. Archived secrets are never +available through MCP. + +Each secret has one category. Category is a plain text field. Tags are separate +search keywords. + +## Secret Fields + +Secret contents are represented as a variable-length list of fields: + +```json +{ + "name": "password", + "value": "...", + "encrypted": true, + "masked": true, + "position": 2 +} +``` + +There are no built-in templates. A secret can represent a login/password pair, +API token, SSH key, card PIN, server credentials, or any other structured text +data. + +Field rules: + +- `name` is the field label. +- `value` is the field value. +- `encrypted=true` means the value is encrypted at rest and is not searchable. +- `masked=true` means the value is hidden by default in UI/API responses. +- `position` controls display order. +- field values may be multiline. +- maximum field value size is 64 KB. + +Metadata, tags, category, field names, and values of fields with +`encrypted=false` are searchable. Values of fields with `encrypted=true` are not +searchable. + +## Versioning + +Secret versioning is required. + +A new version is created when any of the following changes: + +- field list +- field value +- field `encrypted` flag +- field `masked` flag +- field order + +Metadata updates do not create a new version. Metadata includes: + +- `title` +- `purpose` +- `category` +- `tags` +- `source` +- `notes` +- `status` +- access flags +- `archived` + +Old versions are available for viewing and reveal. Revealing an old version must +create an audit event. + +There is no dedicated rollback operation. If a user wants to restore old values, +they update the secret normally, creating a new version. + +The `actual` / `outdated` status belongs to the whole secret, not to individual +versions. + +## Access Flags + +Access is configured globally per secret, not per field. + +Flags: + +- `allow_ui` +- `allow_rest_api` +- `allow_mcp` + +For MCP reveal, both conditions are required: + +- API token has the `reveal` scope. +- secret has `allow_mcp=true`. + +## REST API + +The REST API is JSON-only and versioned under `/api/v1`. + +List and search endpoints return metadata plus non-encrypted fields. Full field +values are returned only by reveal endpoints. + +Pagination uses `offset` and `limit`. + +The OpenAPI schema is a public contract and must include clear schemas, +descriptions, and examples. + +Errors use a stable envelope: + +```json +{ + "error": { + "code": "secret_not_found", + "message": "Secret not found", + "details": {} + } +} +``` + +Required REST surface: + +```text +GET /api/v1/me + +GET /api/v1/secrets +POST /api/v1/secrets +GET /api/v1/secrets/{id} +PATCH /api/v1/secrets/{id} +DELETE /api/v1/secrets/{id} + +POST /api/v1/secrets/{id}/reveal +GET /api/v1/secrets/{id}/versions +GET /api/v1/secrets/{id}/versions/{version_id} +POST /api/v1/secrets/{id}/versions/{version_id}/reveal + +GET /api/v1/categories +GET /api/v1/tags +GET /api/v1/suggestions + +GET /api/v1/audit-events +GET /api/v1/secrets/{id}/audit-events + +GET /api/v1/api-tokens +POST /api/v1/api-tokens +DELETE /api/v1/api-tokens/{id} + +POST /api/v1/export +POST /api/v1/import +DELETE /api/v1/account-data +``` + +## API Tokens + +External clients use `gnexus-creds` API tokens. `gnexus-auth` is used for user +login, not as the long-term token source for third-party clients. + +API token rules: + +- created by the user in UI. +- has a user-defined name. +- has scopes. +- is shown only once after creation. +- only a token hash is stored. +- does not expire. +- can be revoked/deleted. + +Scopes: + +- `read` +- `reveal` +- `write` +- `admin` +- `mcp` + +## MCP + +MCP is part of the same FastAPI application. The first supported transport is +HTTP/SSE. `stdio` is out of scope because the AI agent and MCP server are +expected to run on different servers. + +MCP uses the same API token model with the additional `mcp` scope. + +MCP tools: + +- `search_secrets` +- `get_secret` +- `reveal_secret` +- `create_secret` +- `update_secret` +- `set_secret_status` +- `archive_secret` + +Archived secrets are never available through MCP. + +MCP search without reveal is not audited. MCP reveal and write operations are +audited with `channel=mcp`. + +## Authentication and Sessions + +User login goes through `gnexus-auth` OAuth authorization code flow with PKCE. +`gnexus-creds` acts as the backend/BFF for its UI: + +1. redirect user to `gnexus-auth`; +2. receive OAuth callback; +3. exchange code for token set through `gnexus-auth-client-py`; +4. fetch `/oauth/userinfo`; +5. create or update the local user; +6. create a server-side session; +7. set a session cookie for the UI. + +UI requests use the server-side session cookie. + +Third-party REST and MCP clients use `gnexus-creds` API tokens. + +`gnexus-auth` is the source of truth for user profile data. Local user data is a +cache/mapping. + +Local user fields: + +- local `id` +- `auth_subject` +- `email` +- `display_name` +- profile/avatar data as needed +- `status` +- `created_at` +- `last_seen_at` + +Profile/status changes are received through `gnexus-auth` webhooks. + +If a user is deleted or blocked in `gnexus-auth`, login is blocked, local data +is preserved, and the local user is marked `disabled`. + +Roles come from `gnexus-auth`. MVP roles: + +- `user` +- `admin` + +Admin UI and admin API access must be enforced on the backend. + +## Encryption + +The service master key is provided through an environment variable. + +Each user gets a separate user encryption key on first login. The user key is +encrypted by the master key and stored in the database. + +User key rotation is out of scope for MVP. + +Only values of fields with `encrypted=true` are encrypted. Metadata, tags, +category, field names, and non-encrypted field values are stored in plaintext. + +The crypto implementation should store enough metadata for future key handling, +including key id, nonce, ciphertext, and algorithm. + +## Audit + +Audit is required. + +Events to log: + +- secret created +- metadata updated +- new version created +- reveal +- archive / unarchive +- status changed +- hard delete +- API token created +- API token revoked +- MCP access where applicable +- failed access attempt +- export created + +Search/list events are not audited. MCP search without reveal is not audited. + +Failed access attempts must be rate-limited or aggregated so repeated identical +bot traffic does not flood the audit log. At minimum, only the first repeated +event in a suppression window should be stored. + +Audit event data: + +- actor user +- channel: `ui`, `rest`, or `mcp` +- IP when available +- user agent when available +- API token/client id when applicable +- secret id when applicable +- action +- timestamp +- short metadata diff when applicable +- snapshot data needed to understand events after secret deletion + +Secret values must never be stored in audit events. + +Audit is available: + +- as a global list for the user; +- filtered by secret. + +Audit retention is 1 year. Users cannot edit or clear audit logs. + +## Export and Import + +Users can export all their data in the service's own JSON format. Export +includes decrypted field values. + +Export is a high-risk operation and must require explicit confirmation in UI. +It must create an audit event. + +Import supports only JSON files created by `gnexus-creds` export. CSV and +third-party import formats are out of scope. + +## Deletion + +Secret deletion is hard delete. Secret versions are deleted with the secret. +Audit events remain and must include enough snapshot data to remain meaningful. + +Users can delete all their own `gnexus-creds` data. Dangerous actions require a +confirmation modal in UI. + +## UI + +The UI is implemented with Vue.js and `gnexus-ui-kit`. + +Required screens: + +- login/session bootstrap +- dashboard/overview with search +- secrets list +- secret detail +- create/edit form +- reveal/copy controls +- version history screen +- audit list +- API token management +- export/import +- settings/delete data +- admin UI + +The main screen after login is dashboard/overview plus search. + +Secret list columns: + +- title +- purpose +- category +- tags +- status +- access flags +- created at +- updated at + +The list may show non-encrypted fields. + +Creating a secret uses one form. Required/important fields appear first. +Optional fields appear below. + +Reveal behavior: + +- values are shown in the card after pressing a reveal button. +- values can be copied without visual reveal. +- no additional password/PIN confirmation is required before reveal. + +Encrypted fields display the field name and reveal/copy buttons. + +Categories and tags use text input with autocomplete from existing values. + +Bulk actions are out of scope for MVP. + +Localization is required, but MVP ships only English. User locale should be read +from `gnexus-auth` when available. + +Accessibility requirements: + +- keyboard navigation for core flows; +- visible focus states; +- labels for controls; +- sufficient contrast; +- no formal WCAG audit required for MVP. + +## Admin + +Admin features are based on roles from `gnexus-auth`. + +Admin capabilities: + +- list users; +- view user profile/status; +- view user-related system audit; +- basic health/ready diagnostics. + +Admins must not get access to decrypted user secrets by default. + +## Runtime and Deployment + +Production domain: + +- `https://creds.gnexus.space` + +Auth domains: + +- production: `https://auth.gnexus.space` +- development: `http://gnexus-auth.local` + +Runtime choices: + +- Python + FastAPI +- PostgreSQL only +- no Redis for MVP +- server-side sessions in PostgreSQL +- OAuth state/PKCE storage in PostgreSQL +- rate limit state in PostgreSQL +- JSON application logs +- `/health` and `/ready` + +Docker is not required at the beginning of development. The project must be +containerized before production deployment. + +Backup and restore are outside the application scope.