# Deployment and MCP Connection Guide

This document describes how to deploy `gnexus-creds` and connect the MCP server
to an AI agent.

## Runtime Components

`gnexus-creds` is one FastAPI application that serves:

- Vue UI from the built frontend bundle.
- REST API under `/api/v1`.
- OAuth session routes under `/auth`.
- `gnexus-auth` profile webhook under `/webhooks/gnexus-auth`.
- Legacy HTTP/SSE MCP adapter under `/mcp`.
- Official MCP Streamable HTTP endpoint under `/mcp-protocol/`.

PostgreSQL is required. SQLite is not supported for production.

## Required Environment

Create `.env` from `.env.example` and set real values:

```env
GNEXUS_CREDS_ENV=production
GNEXUS_CREDS_DATABASE_URL=postgresql+psycopg://gnexus_creds:change-me@postgres:5432/gnexus_creds
GNEXUS_CREDS_MASTER_KEY=replace-with-a-long-random-master-key
GNEXUS_CREDS_SESSION_SECRET=replace-with-a-long-random-session-secret
GNEXUS_CREDS_SESSION_COOKIE_NAME=gnexus_creds_session

GNEXUS_AUTH_BASE_URL=https://auth.gnexus.space
GNEXUS_AUTH_CLIENT_ID=replace-with-client-id
GNEXUS_AUTH_CLIENT_SECRET=replace-with-client-secret
GNEXUS_AUTH_REDIRECT_URI=https://creds.gnexus.space/auth/callback
GNEXUS_AUTH_WEBHOOK_SECRET=replace-with-webhook-secret

GNEXUS_CREDS_MCP_RESOURCE_URL=https://creds.gnexus.space/mcp-protocol/

GNEXUS_CREDS_RATE_LIMIT_WINDOW_SECONDS=60
GNEXUS_CREDS_RATE_LIMIT_MAX_SENSITIVE_REQUESTS=120
```

For local development on port `8018`, use HTTP values:

```env
GNEXUS_CREDS_ENV=development
GNEXUS_CREDS_DATABASE_URL=postgresql+psycopg://gnexus_creds:gnexus_creds@127.0.0.1:5432/gnexus_creds
GNEXUS_CREDS_MASTER_KEY=local-long-random-master-key
GNEXUS_CREDS_SESSION_SECRET=local-long-random-session-secret

GNEXUS_AUTH_BASE_URL=http://gnexus-auth.local
GNEXUS_AUTH_CLIENT_ID=gnexus-creds
GNEXUS_AUTH_CLIENT_SECRET=replace-with-local-client-secret
GNEXUS_AUTH_REDIRECT_URI=http://localhost:8018/auth/callback
GNEXUS_AUTH_WEBHOOK_SECRET=replace-with-local-webhook-secret

GNEXUS_CREDS_MCP_RESOURCE_URL=http://localhost:8018/mcp-protocol/
```

Production mode fails fast if default secrets are used or if public auth/MCP
URLs do not use HTTPS.

## gnexus-auth Integration

Register `gnexus-creds` as an OAuth client in `gnexus-auth`.

Local development values:

- Client ID: `gnexus-creds`, or the value from `GNEXUS_AUTH_CLIENT_ID`.
- Client secret: the value from `GNEXUS_AUTH_CLIENT_SECRET`.
- Redirect URI: `http://localhost:8018/auth/callback`.
- Requested scopes: `openid`, `email`, `profile`.
- Webhook URL: `http://localhost:8018/webhooks/gnexus-auth`.
- Webhook secret: the same value as `GNEXUS_AUTH_WEBHOOK_SECRET`.

Production values:

- Redirect URI: `https://creds.gnexus.space/auth/callback`.
- Webhook URL: `https://creds.gnexus.space/webhooks/gnexus-auth`.
- Auth base URL: `https://auth.gnexus.space`.

User profile data and user status come from `gnexus-auth`. The supported system
roles are:

- `user`
- `admin`

If `gnexus-auth` sends a disabled, blocked, or deleted status, `gnexus-creds`
keeps the local user data but blocks login.

## Local Development Deploy

Install backend dependencies:

```bash
uv sync --extra dev
```

Start PostgreSQL through Docker Compose:

```bash
docker-compose up -d postgres
```

Run migrations:

```bash
uv run alembic upgrade head
```

Build the frontend bundle served by FastAPI:

```bash
cd frontend
npm install
npm run build
cd ..
```

Start the server on port `8018`:

```bash
uv run uvicorn gnexus_creds.main:app --host 0.0.0.0 --port 8018 --reload
```

Open the UI:

```text
http://localhost:8018/
```

Login starts through:

```text
http://localhost:8018/auth/login
```

## Docker Compose Deploy

Prepare `.env`, then build and start PostgreSQL:

```bash
docker-compose build app
docker-compose up -d postgres
```

Run migrations explicitly:

```bash
docker-compose run --rm app alembic upgrade head
```

Start the app:

```bash
docker-compose up -d app
```

The included `docker-compose.yml` maps the app to host port `8000`:

```yaml
ports:
  - "8000:8000"
```

For local testing on `8018`, change it to:

```yaml
ports:
  - "8018:8000"
```

The Docker image contains the built Vue UI. Migrations are not run
automatically by the container.

## Reverse Proxy

The reverse proxy must forward these paths to the FastAPI app:

- `/`
- `/api/v1/*`
- `/auth/*`
- `/webhooks/*`
- `/mcp/*`
- `/mcp-protocol/*`
- `/health`
- `/ready`

For MCP Streamable HTTP, do not buffer or rewrite request bodies. Keep the
trailing slash in the MCP URL:

```text
https://creds.gnexus.space/mcp-protocol/
```

## Operational Checks

Liveness:

```bash
curl -fsS http://localhost:8018/health
```

Readiness, including database connectivity:

```bash
curl -fsS http://localhost:8018/ready
```

Apply migrations before every deploy:

```bash
uv run alembic upgrade head
```

or inside Docker:

```bash
docker-compose run --rm app alembic upgrade head
```

## Preparing MCP Access

MCP uses `gnexus-creds` API tokens, not `gnexus-auth` access tokens.

1. Log in to the UI.
2. Open `Tokens`.
3. Create a token for the AI agent.
4. Select scopes based on the agent's required access:

```text
mcp, read
```

Allows search and metadata reads.

```text
mcp, read, reveal
```

Allows search, metadata reads, and decrypted reveal.

```text
mcp, read, reveal, write
```

Allows full MCP access, including creating and updating secrets.

The `admin` scope is not required for normal MCP access.

Only secrets with `allow_mcp=true` are visible through MCP. Archived secrets are
not visible through normal MCP access.

## Recommended MCP Endpoint

Use the official Streamable HTTP endpoint when the AI agent supports MCP over
HTTP:

```text
https://creds.gnexus.space/mcp-protocol/
```

Local development:

```text
http://localhost:8018/mcp-protocol/
```

The agent must send:

```http
Authorization: Bearer <gnexus-creds-api-token>
```

Generic agent configuration shape:

```json
{
  "mcpServers": {
    "gnexus-creds": {
      "type": "streamable-http",
      "url": "https://creds.gnexus.space/mcp-protocol/",
      "headers": {
        "Authorization": "Bearer <gnexus-creds-api-token>"
      }
    }
  }
}
```

Some agents name the transport `http`, `streamable_http`, or `mcp-http`.
Use the transport name expected by the concrete agent, but keep the URL and
Authorization header unchanged.

`stdio` is not supported and should not be used: the AI agent and
`gnexus-creds` server are expected to run on different hosts.

## Legacy MCP HTTP/SSE Adapter

Use the legacy adapter only for clients that do not support MCP Streamable HTTP.

Discovery stream:

```bash
curl -N \
  -H "Authorization: Bearer $GNEXUS_CREDS_MCP_TOKEN" \
  http://localhost:8018/mcp/sse
```

Search secrets:

```bash
curl -sS \
  -H "Authorization: Bearer $GNEXUS_CREDS_MCP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"arguments":{"q":"github","limit":5}}' \
  http://localhost:8018/mcp/tools/search_secrets
```

Reveal one secret:

```bash
curl -sS \
  -H "Authorization: Bearer $GNEXUS_CREDS_MCP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"arguments":{"secret_id":"<secret-uuid>"}}' \
  http://localhost:8018/mcp/tools/reveal_secret
```

Create a secret:

```bash
curl -sS \
  -H "Authorization: Bearer $GNEXUS_CREDS_MCP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "arguments": {
      "title": "Example",
      "purpose": "example.com",
      "category": "site",
      "tags": ["example"],
      "allow_ui": true,
      "allow_rest_api": true,
      "allow_mcp": true,
      "fields": [
        {"name": "username", "value": "user@example.com", "encrypted": false},
        {"name": "password", "value": "secret-password", "encrypted": true, "masked": true}
      ]
    }
  }' \
  http://localhost:8018/mcp/tools/create_secret
```

## MCP Tools

Current tools:

- `search_secrets`
- `get_secret`
- `reveal_secret`
- `create_secret`
- `update_secret`
- `set_secret_status`
- `archive_secret`

`search_secrets` supports pagination with `offset` and `limit`. The maximum
`limit` is 50. If many secrets match, iterate with `offset` increments.

Search returns metadata and unencrypted fields only. Encrypted values are
returned only by `reveal_secret`.

Reveal and write actions create audit events with `channel=mcp`.

## Backup and Restore

Backups are managed from the admin panel (`Admin` tab → `Backups`).

### Backup directory

By default, backups are written to `./backups`. In Docker Compose, this is a
persistent volume mounted at `/app/backups`.

Set a custom path via environment variable:

```env
GNEXUS_CREDS_BACKUP_DIR=/var/backups/gnexus-creds
```

### Create backup

From the UI:

1. Open the `Admin` tab.
2. Click `Create backup`.

From the command line (inside the container or on the host with `pg_dump`):

```bash
docker-compose exec app python -c "from gnexus_creds.backup import create_backup; print(create_backup())"
```

Or via the admin API (admin user required):

```bash
curl -fsS -X POST \
  -H "Cookie: gnexus_creds_session=..." \
  http://localhost:8000/api/v1/admin/backup
```

### List backups

UI: `Admin` tab shows the backup table with size and creation time.

API:

```bash
curl -fsS \
  -H "Cookie: gnexus_creds_session=..." \
  http://localhost:8000/api/v1/admin/backups
```

### Download backup

Click `Download` in the UI, or use the direct link:

```bash
curl -fsS \
  -H "Cookie: gnexus_creds_session=..." \
  -O \
  http://localhost:8000/api/v1/admin/backups/backup_20260520_120000.sql
```

### Restore backup

**Warning:** restore overwrites the current database. All existing data is
replaced.

From the UI:

1. Open the `Admin` tab.
2. Click `Restore` next to the desired backup.
3. Confirm in the modal.

After restore, reload the page to refresh the UI state.

From the command line:

```bash
docker-compose exec app python -c "
from gnexus_creds.backup import restore_backup
restore_backup('/app/backups/backup_20260520_120000.sql')
"
```

Or via API:

```bash
curl -fsS -X POST \
  -H "Cookie: gnexus_creds_session=..." \
  -H "Content-Type: application/json" \
  -d '{"filename": "backup_20260520_120000.sql"}' \
  http://localhost:8000/api/v1/admin/restore
```

### Supported engines

- **PostgreSQL** — dumps with `pg_dump`, restores with `psql`. The `pg_dump`
  binary is included in the Docker image.
- **SQLite** — file copy (not recommended for production).

## Troubleshooting

`401 Unauthorized`:

- Token is missing, revoked, or copied incorrectly.
- The AI agent is not sending the `Authorization` header.

`403 Forbidden`:

- Token does not have the `mcp` scope.
- Tool-specific scope is missing: `read`, `reveal`, or `write`.
- User is disabled by `gnexus-auth`.

Secret is missing from MCP search:

- Secret has `allow_mcp=false`.
- Secret is archived.
- Search query targets an encrypted field value. Encrypted values are not
  searchable.

OAuth callback fails:

- `GNEXUS_AUTH_REDIRECT_URI` does not exactly match the client redirect URI in
  `gnexus-auth`.
- `GNEXUS_AUTH_CLIENT_SECRET` is wrong.
- `GNEXUS_AUTH_BASE_URL` points to the wrong auth instance.

Production app refuses to start:

- `GNEXUS_CREDS_ENV=production` requires HTTPS for auth redirect and MCP
  resource URLs.
- Default secrets from `.env.example` must be replaced.
