Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0edbfcd
feat(oob): replace raw-mode input with secureInput in auth.ts
Apr 13, 2026
55838c6
feat(credential-store): add credential_store toolset and {{secure.NAM…
Apr 14, 2026
5f7f821
feat(oob): add secure-input utility and @inquirer/password dependency
Apr 14, 2026
18b99df
fix(credential-store): address Phase 2 review findings (security + co…
Apr 14, 2026
a16f868
fix(credential-store): add migration path for old encrypted credentia…
Apr 14, 2026
b03c653
fix: replace stale salt-file test with key-file persistence test
Apr 15, 2026
60f6105
fix: replace stale salt-file test with key-file persistence test
Apr 15, 2026
6dda598
fix: resolve TypeScript errors in auth.ts
Apr 16, 2026
e3d994f
fix(crypto): store key at salt path in hex format
Apr 19, 2026
7998c67
fix(oob): rename 'API key' label to 'Secure Value' in auth terminal p…
Apr 20, 2026
44e183f
feat(register-member): resolve {{secure.NAME}} tokens in password field
Apr 20, 2026
91b288b
review: oob-improvements feedback
Apr 20, 2026
c14255b
fix(security): resolve all H/M/L findings from security review
Apr 20, 2026
bfcaceb
fix(H2): redact credentials in monitor_task log output via task-scope…
Apr 20, 2026
3b199a2
chore: update feedback.md SHA references to current commits (c14255b,…
Apr 20, 2026
06e2605
review: re-review oob-improvements
Apr 20, 2026
47a6fcd
feat: resolve {{secure.NAME}} tokens in provision-vcs-auth and provis…
Apr 20, 2026
9f131a4
review: final review of provision token resolution
Apr 20, 2026
d78343f
feat(update-member): resolve {{secure.NAME}} tokens in password field
Apr 20, 2026
99295c5
review: update-member secure token resolution
Apr 20, 2026
3fff4f4
docs: comprehensive credential store documentation across README, fle…
Apr 20, 2026
3c261e7
chore: regenerate llms-full.txt [skip ci]
github-actions[bot] Apr 20, 2026
ffba73f
fix(security): reject {{secure.NAME}} tokens in execute_prompt; fix docs
Apr 20, 2026
8916428
review: execute_prompt security guard and docs
Apr 20, 2026
0b05dc9
chore: regenerate llms-full.txt [skip ci]
github-actions[bot] Apr 20, 2026
fabec56
feat(A): OOB fallback for provision_vcs_auth and provision_auth
Apr 20, 2026
28232a7
feat(A): OOB fallback for provision_vcs_auth and provision_auth
Apr 20, 2026
75bdcf9
feat(E): resolve {{secure.NAME}} tokens as PEM content in setup_git_app
Apr 20, 2026
78d4946
chore: mark E complete in progress.json
Apr 20, 2026
52595f6
review: Features A and E
Apr 20, 2026
14931c8
polish: generic secure-input message, rename smoke script, document {…
Apr 20, 2026
21e8eed
polish: fix {{secure.NAME}} warning example in skills
Apr 20, 2026
7bd7683
cleanup: remove fleet control files
Apr 20, 2026
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
19 changes: 0 additions & 19 deletions AGENTS.md

This file was deleted.

18 changes: 0 additions & 18 deletions CLAUDE.md

This file was deleted.

38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ Apra Fleet is an MCP server that agentic coding systems connect to. It manages a
| `setup_git_app` | One-time setup: register a GitHub App for scoped git token minting |
| `provision_vcs_auth` | Deploy VCS credentials to a member (GitHub App, Bitbucket, Azure DevOps) |
| `revoke_vcs_auth` | Remove deployed VCS credentials from a member |
| `credential_store_set` | Store a secret credential for use in commands (entered OOB — never in chat) |
| `credential_store_list` | List stored credential names (values are never returned) |
| `credential_store_delete` | Delete a stored credential |

### Infrastructure

Expand All @@ -281,6 +284,41 @@ Apra Fleet is an MCP server that agentic coding systems connect to. It manages a
| `shutdown_server` | Gracefully shut down the MCP server |
| `version` | Report server version |

## Secure Credentials

Secrets never enter the LLM conversation or logs. Fleet's credential store keeps plaintext isolated: you enter it once in a separate terminal window, it's encrypted at rest, and commands receive it as a resolved value — never as readable text.

**Three-step workflow:**

```
# 1. Store once — Fleet opens an OOB terminal prompt, never asks in chat
credential_store_set name=github_pat

# 2. Use in execute_command — {{secure.NAME}} is resolved only in commands, never in prompts
execute_command command="curl -H 'Authorization: Bearer {{secure.github_pat}}' https://api.github.com/user"

# 3. Output is automatically redacted before it reaches the LLM
# Output: Authorization: Bearer [REDACTED:github_pat]
```

**Tools that support `{{secure.NAME}}` token substitution:**

- `execute_command` — shell commands on any member
- `register_member` — SSH password field during registration
- `update_member` — updating stored member passwords
- `provision_vcs_auth` — VCS token fields (GitHub PAT, Bitbucket token, Azure DevOps PAT)
- `provision_auth` — LLM API key fields

`execute_prompt` does **not** support `{{secure.NAME}}` — secrets must never be passed to LLM prompts. In `execute_prompt`, reference credentials by name only (e.g. `"authenticate using credential github_pat"`); the member resolves the token in its own `execute_command` calls.

**Credential store tools:**

| Tool | What it does |
|------|-------------|
| `credential_store_set` | Prompt for a secret OOB and store it encrypted under a name |
| `credential_store_list` | List stored credential names — values are never returned |
| `credential_store_delete` | Remove a stored credential by name |

## Multi-Provider Fleets

### Provisioning auth for non-Claude members
Expand Down
11 changes: 11 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ Email **contact@apralabs.com** with:

We will coordinate disclosure timing with you and credit reporters in release notes unless you prefer to remain anonymous.

## Credential Handling

Fleet is designed so that secrets never enter the LLM conversation or appear in logs. The following controls are in place:

- **Encryption at rest** — credentials stored via `credential_store_set` are encrypted with AES-256-GCM. Plaintext is never written to disk or config files.
- **Out-of-band collection** — secret values are always collected via a separate terminal window (OOB prompt), not through the chat interface. The LLM never sees the value during input.
- **LLM context isolation** — `{{secure.NAME}}` tokens are resolved server-side, after the LLM has finished generating the command. The plaintext value is substituted at execution time, not during prompt construction.
- **Output redaction** — any command output that contains a stored credential's plaintext value is automatically redacted to `[REDACTED:NAME]` before the result is returned to the LLM. This applies to stdout, stderr, and structured output.
- **Network egress policy** — each credential can be assigned an egress policy (`allow`, `confirm`, `deny`) controlling whether it can be sent to external hosts. The server enforces this before executing commands that would transmit the resolved value over the network.
- **No value retrieval** — `credential_store_list` returns credential names only. There is no API to retrieve stored plaintext — secrets are write-once from the credential store's perspective.

## Out of Scope

The following are not considered security vulnerabilities for this project:
Expand Down
38 changes: 38 additions & 0 deletions llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ Apra Fleet is an MCP server that agentic coding systems connect to. It manages a
| `setup_git_app` | One-time setup: register a GitHub App for scoped git token minting |
| `provision_vcs_auth` | Deploy VCS credentials to a member (GitHub App, Bitbucket, Azure DevOps) |
| `revoke_vcs_auth` | Remove deployed VCS credentials from a member |
| `credential_store_set` | Store a secret credential for use in commands (entered OOB — never in chat) |
| `credential_store_list` | List stored credential names (values are never returned) |
| `credential_store_delete` | Delete a stored credential |

### Infrastructure

Expand All @@ -284,6 +287,41 @@ Apra Fleet is an MCP server that agentic coding systems connect to. It manages a
| `shutdown_server` | Gracefully shut down the MCP server |
| `version` | Report server version |

## Secure Credentials

Secrets never enter the LLM conversation or logs. Fleet's credential store keeps plaintext isolated: you enter it once in a separate terminal window, it's encrypted at rest, and commands receive it as a resolved value — never as readable text.

**Three-step workflow:**

```
# 1. Store once — Fleet opens an OOB terminal prompt, never asks in chat
credential_store_set name=github_pat

# 2. Use in execute_command — {{secure.NAME}} is resolved only in commands, never in prompts
execute_command command="curl -H 'Authorization: Bearer {{secure.github_pat}}' https://api.github.com/user"

# 3. Output is automatically redacted before it reaches the LLM
# Output: Authorization: Bearer [REDACTED:github_pat]
```

**Tools that support `{{secure.NAME}}` token substitution:**

- `execute_command` — shell commands on any member
- `register_member` — SSH password field during registration
- `update_member` — updating stored member passwords
- `provision_vcs_auth` — VCS token fields (GitHub PAT, Bitbucket token, Azure DevOps PAT)
- `provision_auth` — LLM API key fields

`execute_prompt` does **not** support `{{secure.NAME}}` — secrets must never be passed to LLM prompts. In `execute_prompt`, reference credentials by name only (e.g. `"authenticate using credential github_pat"`); the member resolves the token in its own `execute_command` calls.

**Credential store tools:**

| Tool | What it does |
|------|-------------|
| `credential_store_set` | Prompt for a secret OOB and store it encrypted under a name |
| `credential_store_list` | List stored credential names — values are never returned |
| `credential_store_delete` | Remove a stored credential by name |

## Multi-Provider Fleets

### Provisioning auth for non-Claude members
Expand Down
142 changes: 140 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
],
"license": "Apache-2.0",
"dependencies": {
"@inquirer/password": "^5.0.11",
"@modelcontextprotocol/sdk": "^1.27.0",
"smol-toml": "^1.6.1",
"ssh2": "^1.17.0",
Expand Down
9 changes: 9 additions & 0 deletions scripts/smoke-secure-input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { secureInput } from '../src/utils/secure-input.js';

try {
const value = await secureInput({ prompt: 'Password', allowEmpty: true });
console.log(`Captured: ${value}`);
console.log(`Length: ${value.length}`);
} catch (err: any) {
console.log(`Cancelled: ${err.message}`);
}
Loading
Loading