Skip to content

REST API Reference

MaestroVault includes a REST API server that listens on a Unix domain socket. This allows other tools, scripts, and services on the same machine to interact with secrets programmatically.

Starting the Server

mav serve

Default socket: ~/.maestrovault/maestrovault.sock

Custom socket:

mav serve --socket /tmp/mav.sock

The server runs in the foreground and shuts down gracefully on Ctrl+C (SIGINT/SIGTERM). The socket file is cleaned up on exit.

Authentication

All endpoints except /v1/health require a Bearer token in the Authorization header:

Authorization: Bearer mvt_abc123...

Create tokens with:

mav token create --name "my-token" --scope read,write

Scopes

Scope Grants
read Get, list, search, info
write Set, edit, delete
generate Password generation
admin Token management (implicitly grants all other scopes)

Endpoints

Health Check

GET /v1/health

No authentication required.

Response:

{
  "status": "ok",
  "time": "2026-03-13T12:00:00Z"
}

List Secrets

GET /v1/secrets

Scope: read

Query Parameters:

Parameter Description
env Filter by environment
metadata_key Filter by metadata key
metadata_value Filter by metadata value (requires metadata_key)

Response:

[
  {
    "name": "db-password",
    "environment": "prod",
    "metadata": {"service": "postgres"},
    "created_at": "2026-03-13T10:00:00Z",
    "updated_at": "2026-03-13T10:00:00Z"
  }
]

Note

List does not decrypt values. Use GET by name to retrieve the plaintext.


Get Secret

GET /v1/secrets/{name}

Scope: read

Query Parameters:

Parameter Description
env Environment (default: empty string)

Response:

{
  "name": "db-password",
  "environment": "prod",
  "value": "s3cret",
  "metadata": {"service": "postgres"},
  "fields": {
    "host": "db.example.com",
    "port": "5432",
    "username": "admin"
  },
  "field_count": 3,
  "created_at": "2026-03-13T10:00:00Z",
  "updated_at": "2026-03-13T10:00:00Z"
}

The value field is omitted if the secret has no main value (fields-only entry). fields and field_count are omitted when there are no fields.


Set Secret

PUT /v1/secrets/{name}

Scope: write

Query Parameters:

Parameter Description
env Environment (default: empty string)

Request Body:

{
  "value": "my-secret-value",
  "metadata": {"service": "api", "team": "backend"}
}

Response (201):

{
  "status": "stored",
  "name": "my-secret"
}

Edit Secret

PATCH /v1/secrets/{name}

Scope: write

Partial update -- omitted fields are preserved.

Query Parameters:

Parameter Description
env Environment (default: empty string)

Request Body:

{
  "value": "new-value",
  "metadata": {"service": "api"}
}

Response:

{
  "status": "updated",
  "name": "my-secret"
}

Delete Secret

DELETE /v1/secrets/{name}

Scope: write

Query Parameters:

Parameter Description
env Environment (default: empty string)

Response:

{
  "status": "deleted",
  "name": "my-secret"
}

Secret Fields

Secrets can have an optional set of individually encrypted key-value fields in addition to (or instead of) the main value. Field keys are plaintext; field values are encrypted with per-field envelope encryption.

Set a Single Field

PUT /v1/secrets/{name}/fields/{field}

Scope: write

Query Parameters:

Parameter Description
env Environment (default: empty string)

Request Body:

{
  "value": "field-value"
}

If the parent secret does not exist, it is created automatically (with no main value).

Response (201):

{
  "status": "stored",
  "name": "my-secret",
  "field": "username"
}

Get a Single Field

GET /v1/secrets/{name}/fields/{field}

Scope: read

Query Parameters:

Parameter Description
env Environment (default: empty string)

Response:

{
  "name": "my-secret",
  "field": "username",
  "value": "admin"
}

Get All Fields

GET /v1/secrets/{name}/fields

Scope: read

Query Parameters:

Parameter Description
env Environment (default: empty string)

Response:

{
  "host": "db.example.com",
  "port": "5432",
  "username": "admin"
}

Returns a JSON object mapping field keys to their decrypted values. Returns an empty object {} if no fields exist.

#### Set Multiple Fields
PUT /v1/secrets/{name}/fields
**Scope:** `write`

**Query Parameters:**

| Parameter | Description |
|-----------|-------------|
| `env` | Environment (default: empty string) |

**Request Body:**

```json
{
  "fields": {
    "host": "db.example.com",
    "port": "5432",
    "username": "admin"
  }
}

Creates or updates multiple fields at once. If the parent secret does not exist, it is created automatically.

Response (201):

{
  "status": "stored",
  "name": "my-secret",
  "field_count": 3
}

Delete a Field

DELETE /v1/secrets/{name}/fields/{field}

Scope: write

Query Parameters:

Parameter Description
env Environment (default: empty string)

Response:

{
  "status": "deleted",
  "name": "my-secret",
  "field": "username"
}

Scope: read

Searches secret names, environments, and metadata.

Response: Same format as List Secrets.


Generate Password

POST /v1/generate

Scope: generate

Request Body:

{
  "name": "wifi-password",
  "environment": "home",
  "length": 24,
  "uppercase": true,
  "lowercase": true,
  "digits": true,
  "symbols": false,
  "metadata": {"type": "wifi"}
}

All fields are optional. Defaults: length 32, all character sets enabled. If name is provided, the password is stored as a secret. Include environment to scope the stored secret.

Response:

{
  "password": "xK9mP2vL...",
  "name": "wifi-password",
  "stored": true
}

Vault Info

GET /v1/info

Scope: read

Response:

{
  "dir": "/Users/you/.maestrovault",
  "db_path": "/Users/you/.maestrovault/vault.db",
  "db_size_bytes": 32768,
  "secret_count": 42
}

List Tokens

GET /v1/tokens

Scope: admin

Response:

[
  {
    "id": "a1b2c3d4e5f6a7b8",
    "name": "ci-read",
    "scopes": ["read"],
    "created_at": "2026-03-13T10:00:00Z",
    "expires_at": null,
    "last_used_at": "2026-03-13T11:30:00Z"
  }
]

Create Token

POST /v1/tokens

Scope: admin

Request Body:

{
  "name": "deploy-token",
  "scopes": ["read", "write"],
  "expires_in": "720h"
}

expires_in is optional. Use Go duration format (24h, 720h, etc.) or omit for no expiry.

Response (201):

{
  "token": "mvt_abc123...",
  "id": "a1b2c3d4e5f6a7b8",
  "name": "deploy-token",
  "scopes": ["read", "write"],
  "expires_at": "2026-04-12T10:00:00Z"
}

Warning

The plaintext token is only returned once. Store it securely.


Revoke Token

DELETE /v1/tokens/{id}

Scope: admin

Response:

{
  "status": "revoked",
  "id": "a1b2c3d4e5f6a7b8"
}

Error Responses

All errors return JSON:

{
  "error": "description of the problem"
}
Status Meaning
400 Bad request (missing/invalid parameters)
401 Unauthorized (missing/invalid token)
403 Forbidden (insufficient scope)
404 Not found
500 Internal server error

Using with curl

Since the API uses a Unix socket, use curl's --unix-socket flag:

# Health check
curl --unix-socket ~/.maestrovault/maestrovault.sock \
  http://localhost/v1/health

# Get a secret (with environment)
curl --unix-socket ~/.maestrovault/maestrovault.sock \
  -H "Authorization: Bearer mvt_your_token_here" \
  http://localhost/v1/secrets/db-password?env=prod

# Store a secret
curl --unix-socket ~/.maestrovault/maestrovault.sock \
  -H "Authorization: Bearer mvt_your_token_here" \
  -X PUT \
  -d '{"value": "s3cret", "metadata": {"service": "db"}}' \
  http://localhost/v1/secrets/my-key?env=dev

Socket Security

The Unix socket is created with 0600 permissions (owner read/write only). Only the user who started the server can connect. This provides OS-level access control without network exposure.