# 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.
