Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@
# AGENT_VAULT_LOGS_MAX_ROWS_PER_VAULT=10000 # Keep at most this many rows per vault (default 10000).
# AGENT_VAULT_LOGS_RETENTION_LOCK=false # When true, ignore any owner UI overrides (operator pin).

# Credential history retention (optional) — how many prior values of a
# built-in credential are kept (per vault+key) after being overwritten.
# See `agent-vault vault credential history`/`rollback`. 0 = unbounded.
# AGENT_VAULT_CREDENTIAL_HISTORY_MAX_VERSIONS=10

# Rate limiting (optional) — tiered limits with sensible defaults.
# Profile: default | strict (≈0.5×) | loose (≈2×) | off (disable all limits).
# AGENT_VAULT_RATELIMIT_PROFILE=default
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Features:
- **Purpose-Built Design**: Existing forward proxies like `mitmproxy` or `squid` require modification to perform credential brokering and integrate well with agents. Agent Vault is purpose-built to work with the ergonomics of all types of agent use-cases with a dedicated CLI, multi-tenancy, and agent-specific roadmap backed by [Infisical](https://github.com/Infisical/infisical).
- **Egress Filtering**: Control which agents should have access to which services and API endpoints on them since authenticated requests flow through Agent Vault.
- **Request Logging**: Inspect authenticated traffic to monitor and diagnose agent behavior.
- **Credential History & Rollback**: Every overwrite of a built-in credential archives the value it replaces, so an accidental overwrite is never destructive. Inspect and restore with `vault credential history`/`rollback`. See [credentials](docs/learn/credentials.mdx#credential-history-and-rollback).

By default, requests not matching any service forward as plain proxy traffic; flip a vault into strict deny mode (`unmatched_host_policy=deny`) to reject them with 403 instead.

Expand Down
23 changes: 23 additions & 0 deletions cmd/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,29 @@ func TestCommandsRegistered(t *testing.T) {
}
}

func TestCredentialSubcommandsRegistered(t *testing.T) {
vaultCmd := findSubcommand(rootCmd, "vault")
if vaultCmd == nil {
t.Fatal("vault command not found")
}
credCmd := findSubcommand(vaultCmd, "credential")
if credCmd == nil {
t.Fatal("credential command not found")
}

registered := make(map[string]bool)
for _, c := range credCmd.Commands() {
registered[c.Name()] = true
}

expected := []string{"list", "get", "set", "delete", "history", "rollback"}
for _, name := range expected {
if !registered[name] {
t.Errorf("expected credential subcommand %q to be registered, but it was not", name)
}
}
}

func TestCASubcommandsRegistered(t *testing.T) {
caCmd := findSubcommand(rootCmd, "ca")
if caCmd == nil {
Expand Down
126 changes: 126 additions & 0 deletions cmd/credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,137 @@ required — there is no project-file or interactive-picker fallback.`,
},
}

var credentialHistoryCmd = &cobra.Command{
Use: "history <key>",
Short: "List archived versions of a credential",
Long: `List past values a credential held before being overwritten, most recent
first, with the timestamp and actor (user/agent) responsible for each
overwrite. Requires member+ role — the same rule as "credential get"/--reveal.

Values are not shown unless --reveal is also passed. Roll one back with
"agent-vault vault credential rollback <key> --version N".

In agent mode (AGENT_VAULT_TOKEN set), AGENT_VAULT_VAULT (or --vault) is
required — there is no project-file or interactive-picker fallback.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
sess, tokenSource, err := resolveSession()
if err != nil {
return err
}

vault, err := resolveVaultForCommand(cmd, tokenSource)
if err != nil {
return err
}
key := args[0]
reveal, _ := cmd.Flags().GetBool("reveal")

reqURL := sess.Address + "/v1/credentials/history?vault=" + url.QueryEscape(vault) + "&key=" + url.QueryEscape(key)
if reveal {
reqURL += "&reveal=true"
}
respBody, err := doAdminRequestWithBody("GET", reqURL, sess.Token, nil)
if err != nil {
return err
}

var result struct {
Versions []struct {
Version int `json:"version"`
ActorType string `json:"actor_type"`
ActorID string `json:"actor_id"`
CreatedAt string `json:"created_at"`
Value string `json:"value"`
} `json:"versions"`
}
if err := json.Unmarshal(respBody, &result); err != nil {
return fmt.Errorf("parsing response: %w", err)
}

if len(result.Versions) == 0 {
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "No archived versions for credential %q in vault %q.\n", key, vault)
return nil
}

t := newTable(cmd.OutOrStdout())
header := table.Row{"VERSION", "REPLACED AT", "ACTOR"}
if reveal {
header = append(header, "VALUE")
}
t.AppendHeader(header)
for _, v := range result.Versions {
actor := "unknown"
if v.ActorType != "" || v.ActorID != "" {
actor = fmt.Sprintf("%s:%s", v.ActorType, v.ActorID)
}
row := table.Row{v.Version, v.CreatedAt, actor}
if reveal {
row = append(row, v.Value)
}
t.AppendRow(row)
}
t.Render()
return nil
},
}

var credentialRollbackCmd = &cobra.Command{
Use: "rollback <key>",
Short: "Restore an archived version of a credential as its current value",
Long: `Restore a prior version of a credential (see "credential history") as its
current value. The value being replaced is itself archived as a new version
first, so a rollback is never destructive — you can always roll back a
rollback. Requires member+ role, same as "credential set".

In agent mode (AGENT_VAULT_TOKEN set), AGENT_VAULT_VAULT (or --vault) is
required — there is no project-file or interactive-picker fallback.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
sess, tokenSource, err := resolveSession()
if err != nil {
return err
}

vault, err := resolveVaultForCommand(cmd, tokenSource)
if err != nil {
return err
}
key := args[0]

version, err := cmd.Flags().GetInt("version")
if err != nil || version <= 0 {
return fmt.Errorf("--version is required and must be a positive integer (see \"credential history %s\")", key)
}

body, err := json.Marshal(map[string]interface{}{
"vault": vault,
"key": key,
"version": version,
})
if err != nil {
return err
}

reqURL := sess.Address + "/v1/credentials/rollback"
if _, err := doAdminRequestWithBody("POST", reqURL, sess.Token, body); err != nil {
return err
}

_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Rolled back credential %q in vault %q to version %d\n", successText("✓"), key, vault, version)
return nil
},
}

func init() {
credentialListCmd.Flags().Bool("reveal", false, "Show decrypted credential values (requires member+ role)")
credentialHistoryCmd.Flags().Bool("reveal", false, "Show decrypted values for each version (requires member+ role)")
credentialRollbackCmd.Flags().Int("version", 0, "Version number to restore (see \"credential history\")")
credentialCmd.AddCommand(credentialListCmd)
credentialCmd.AddCommand(credentialGetCmd)
credentialCmd.AddCommand(credentialSetCmd)
credentialCmd.AddCommand(credentialDeleteCmd)
credentialCmd.AddCommand(credentialHistoryCmd)
credentialCmd.AddCommand(credentialRollbackCmd)
vaultCmd.AddCommand(credentialCmd)
}
25 changes: 24 additions & 1 deletion docs/learn/credentials.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,30 @@ This mode is useful for:
agent-vault vault credential set STRIPE_KEY=sk_test_abc123 --vault my-vault
```

The `vault credential` command (alias: `vault creds`) uses `KEY=VALUE` format. Multiple credentials can be set at once (e.g. `agent-vault vault credential set A=1 B=2`). If `STRIPE_KEY` already exists, it is overwritten.
The `vault credential` command (alias: `vault creds`) uses `KEY=VALUE` format. Multiple credentials can be set at once (e.g. `agent-vault vault credential set A=1 B=2`). If `STRIPE_KEY` already exists, it is overwritten — but the previous value isn't gone, see below.

## Credential history and rollback

Every overwrite of a built-in credential — whether from `vault credential set` or a [proposal](/learn/proposals) being approved — archives the value it replaces first. If a bad value gets written, you can list what changed and roll back:

```bash
# List past versions, most recent first (timestamp + who changed it, not the value)
agent-vault vault credential history STRIPE_KEY --vault my-vault

# Include decrypted values (requires member+ role, same as credential get)
agent-vault vault credential history STRIPE_KEY --vault my-vault --reveal

# Restore version 3 as the current value
agent-vault vault credential rollback STRIPE_KEY --version 3 --vault my-vault
```

Rollback is never destructive: restoring an old version archives whatever was live as a new version first, so you can always roll back a rollback. The number of versions kept per credential is configurable via [`AGENT_VAULT_CREDENTIAL_HISTORY_MAX_VERSIONS`](/self-hosting/environment-variables#credential-history-and-rollback) (default 10).

<Note>
This applies only to the built-in credential store. Vaults backed by an
external [credential store](/learn/credential-stores) (e.g., Infisical) are
read-only from Agent Vault and already have their own versioning upstream.
</Note>

## Delete a credential

Expand Down
27 changes: 27 additions & 0 deletions docs/reference/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ description: "Complete reference for all Agent Vault CLI commands."
| `AGENT_VAULT_LOGS_MAX_AGE_HOURS` | Retention ceiling for the per-vault request log. Default `168` (7 days). Rows older than this are trimmed by a background job every 15 minutes. Only non-secret metadata is stored. |
| `AGENT_VAULT_LOGS_MAX_ROWS_PER_VAULT` | Per-vault row cap for the request log. Default `10000`. Whichever limit (age or rows) fills first wins. Set `0` to disable the cap. |
| `AGENT_VAULT_LOGS_RETENTION_LOCK` | When `true`, owner-UI overrides for log retention are ignored and env values (or defaults) are pinned. |
| `AGENT_VAULT_CREDENTIAL_HISTORY_MAX_VERSIONS` | Maximum number of prior versions kept per built-in credential (per vault + key) after being overwritten. Default `10`. Set `0` to disable pruning. See `agent-vault vault credential history`/`rollback`. |
| `INFISICAL_URL` | Base URL of an Infisical instance. When set, the server constructs a machine-identity client from one of the auth-method groups below and enables `--credential-store=infisical` on `vault create`. See [Credential stores](/learn/credential-stores) for the conceptual overview and [Environment variables: Infisical credential store](/self-hosting/environment-variables#infisical-credential-store) for the priority order when multiple groups are configured. |
| `INFISICAL_UNIVERSAL_AUTH_CLIENT_ID` | Universal Auth client ID. Set together with `INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET`. |
| `INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET` | Universal Auth client secret. Set together with `INFISICAL_UNIVERSAL_AUTH_CLIENT_ID`. |
Expand Down Expand Up @@ -671,6 +672,32 @@ description: "Complete reference for all Agent Vault CLI commands."
|------|---------|-------------|
| `--vault` | `default` | Target vault |
</Accordion>

<Accordion title="agent-vault vault credential history">
```bash
agent-vault vault credential history <KEY> [flags]
```

List past values a credential held before being overwritten, most recent first, with the timestamp and actor responsible for each overwrite. Requires member+ role — the same rule as `credential get`/`--reveal`. Alias: `agent-vault vault creds history`. In agent mode (`AGENT_VAULT_TOKEN` set), `AGENT_VAULT_VAULT` (or `--vault`) is required — there is no project-file or interactive-picker fallback.

| Flag | Default | Description |
|------|---------|-------------|
| `--vault` | `default` | Target vault |
| `--reveal` | `false` | Show decrypted values for each version (requires member+ role) |
</Accordion>

<Accordion title="agent-vault vault credential rollback">
```bash
agent-vault vault credential rollback <KEY> --version <N> [flags]
```

Restore an archived version (see `credential history`) as the credential's current value. Whatever is currently live is itself archived as a new version first, so a rollback is never destructive. Requires member+ role, same as `credential set`. Alias: `agent-vault vault creds rollback`. In agent mode (`AGENT_VAULT_TOKEN` set), `AGENT_VAULT_VAULT` (or `--vault`) is required — there is no project-file or interactive-picker fallback.

| Flag | Default | Description |
|------|---------|-------------|
| `--vault` | `default` | Target vault |
| `--version` | (required) | Version number to restore (see `credential history`) |
</Accordion>
</AccordionGroup>

## Proposals
Expand Down
23 changes: 23 additions & 0 deletions docs/self-hosting/environment-variables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,36 @@ description: "Configuration for deploying an instance of Agent Vault."
| `AGENT_VAULT_LOGS_MAX_AGE_HOURS` | Optional (defaults to `168`) | Maximum age, in hours, of rows retained in the per-vault request log. |
| `AGENT_VAULT_LOGS_MAX_ROWS_PER_VAULT` | Optional (defaults to `10000`) | Maximum number of rows retained per vault in the request log. Set to `0` to disable the row cap. |
| `AGENT_VAULT_LOGS_RETENTION_LOCK` | Optional (defaults to `false`) | Whether to ignore owner-UI overrides for log retention and pin to env values. |
| `AGENT_VAULT_CREDENTIAL_HISTORY_MAX_VERSIONS` | Optional (defaults to `10`) | Maximum number of prior versions kept per built-in credential (per vault + key) after being overwritten. See [Credential history and rollback](/self-hosting/environment-variables#credential-history-and-rollback). Set to `0` to disable pruning (unbounded). |
| `AGENT_VAULT_MAX_RESPONSE_BYTES` | Optional (defaults to `0` = unlimited) | Maximum response body bytes the MITM proxy streams back to agents. Responses are streamed with a small buffer so unlimited is safe. When set and exceeded, the proxy returns 502 or aborts the connection. Overridden by `--max-response-bytes`. |
| `AGENT_VAULT_MAX_REQUEST_BYTES` | Optional (defaults to `1073741824` = 1 GiB) | Maximum request body bytes the MITM proxy forwards to upstreams. Requests exceeding this receive HTTP 413. Overridden by `--max-request-bytes`. |
| `AGENT_VAULT_ISOLATION` | Optional (defaults to `host`) | Default isolation mode for `agent-vault vault run`. One of: `host`, `container` (see [Container isolation](/guides/container-isolation)). Overridden by `--isolation`. |
| `DB_MAX_OPEN_CONNS` | Optional (defaults to `25`) | Maximum number of open Postgres connections per instance. Only applies when `DATABASE_URL` is set. See [connection pooling](/self-hosting/postgres#operational-notes). |
| `DB_MAX_IDLE_CONNS` | Optional (defaults to `10`) | Maximum number of idle Postgres connections kept in the pool per instance. Only applies when `DATABASE_URL` is set. See [connection pooling](/self-hosting/postgres#operational-notes). |
| `DB_CONN_MAX_LIFETIME` | Optional (defaults to `5m`) | Maximum lifetime of a Postgres connection before it is closed and replaced. Go duration string (e.g. `5m`, `1h`). Only applies when `DATABASE_URL` is set. See [connection pooling](/self-hosting/postgres#operational-notes). |

## Credential history and rollback

Every overwrite of a built-in credential (a direct `credential set`, or a proposal being applied) archives the value it replaces before writing the new one, so an accidental overwrite is always recoverable. This applies only to the **built-in** credential store — vaults backed by the [Infisical credential store](/learn/credential-stores) already have their own versioning upstream.

`AGENT_VAULT_CREDENTIAL_HISTORY_MAX_VERSIONS` (default `10`) caps how many archived versions are kept per credential; older ones are pruned automatically as new overwrites happen. Set to `0` to keep every version indefinitely.

Use the CLI to inspect and restore history:

```bash
# List past versions of STRIPE_KEY, most recent first (timestamp + actor, not the value)
agent-vault vault credential history STRIPE_KEY --vault my-vault

# Same, but also show the decrypted value of each version (requires member+ role)
agent-vault vault credential history STRIPE_KEY --vault my-vault --reveal

# Restore version 3 as the current value — this archives whatever is
# currently live as a new version first, so rollback is never destructive
agent-vault vault credential rollback STRIPE_KEY --version 3 --vault my-vault
```

Both commands require member+ role on the vault — the same rule as `credential set`/`credential get --reveal` — so proxy-role agents can never read or rewrite history.

## Email SMTP configuration

Configure SMTP to enable Agent Vault to send emails for verification codes, vault invites, and notifications.
Expand Down
Loading