cgcardona / muse public
auth.md markdown
350 lines 9.4 KB
368bcde6 Add security test coverage and reference documentation Gabriel Cardona <gabriel@tellurstori.com> 9h ago
1 # `muse auth` — Identity Management Reference
2
3 Muse has two primary user types: **humans** and **agents**. Both are
4 first-class identities, authenticated identically. This command manages the
5 full identity lifecycle: login, introspection, and logout.
6
7 ---
8
9 ## Table of Contents
10
11 1. [Why not `muse config set auth.token`?](#why-not-muse-config-set-authtoken)
12 2. [Identity File — `~/.muse/identity.toml`](#identity-file)
13 3. [Commands](#commands)
14 - [muse auth login](#muse-auth-login)
15 - [muse auth whoami](#muse-auth-whoami)
16 - [muse auth logout](#muse-auth-logout)
17 4. [Authentication Flows](#authentication-flows)
18 5. [Token Security Best Practices](#token-security-best-practices)
19 6. [Environment Variables](#environment-variables)
20
21 ---
22
23 ## Why not `muse config set auth.token`?
24
25 Credentials belong to the **machine**, not the repository. Storing a token
26 inside `.muse/config.toml` has three problems:
27
28 1. **Accidental commit** — `.muse/config.toml` is in the repo directory and
29 could be committed to version control, exposing the token to everyone with
30 access.
31 2. **Scope creep** — one machine may work with many repositories; the token is
32 a machine-scoped credential.
33 3. **Tight coupling** — tying credentials to a repo prevents sharing them
34 across projects on the same machine.
35
36 Muse separates these concerns:
37
38 | File | Stores | Scope |
39 |---|---|---|
40 | `.muse/config.toml` | Hub URL (`[hub] url`) | Per repository |
41 | `~/.muse/identity.toml` | Bearer token | Per machine |
42
43 The CLI reads the hub URL from the repo, the token from the machine. The two
44 are combined only at request time — the token is never written to
45 `.muse/config.toml`.
46
47 ---
48
49 ## Identity File
50
51 **Path:** `~/.muse/identity.toml`
52 **Permissions:** `0o600` (read/write owner only)
53 **Directory permissions:** `0o700` (owner only)
54
55 ### File format
56
57 TOML with one section per hub hostname. The section key is the bare hostname
58 (no scheme, no path), always lowercase:
59
60 ```toml
61 ["musehub.ai"]
62 type = "human"
63 name = "Alice"
64 id = "usr_abc123"
65 token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
66
67 ["staging.musehub.ai"]
68 type = "agent"
69 name = "composer-v2"
70 id = "agt_def456"
71 token = "muse_tok_..."
72 capabilities = ["read:*", "write:midi", "commit"]
73 ```
74
75 ### `IdentityEntry` fields
76
77 | Field | Type | Required | Description |
78 |---|---|---|---|
79 | `type` | `"human"` \| `"agent"` | Yes | Identity type |
80 | `token` | `str` | Yes | Bearer token — never logged |
81 | `name` | `str` | No | Display name (human name or agent handle) |
82 | `id` | `str` | No | Hub-assigned identity ID |
83 | `capabilities` | `list[str]` | No | Agent capability strings (empty for humans) |
84
85 ### URL normalisation
86
87 The file is keyed by bare hostname, not full URL. The following all resolve to
88 the same entry:
89
90 ```
91 https://musehub.ai
92 https://musehub.ai/repos/my-song
93 MUSEHUB.AI
94 musehub.ai
95 ```
96
97 Userinfo embedded in the URL (`user:password@musehub.ai`) is stripped before
98 use as the key — credentials are never stored inside the hostname key.
99
100 ### Security properties
101
102 - The file is written with `0o600` permissions **from byte zero** using
103 `os.open()` + `os.fchmod()`, eliminating the TOCTOU window that
104 `write_text()` + `chmod()` creates.
105 - Writes are atomic: data goes to a temp file, then `os.replace()` renames it
106 over the target. A kill signal during write leaves the old file intact.
107 - A symlink at the target path is refused — symlink-based credential-overwrite
108 attacks are blocked.
109 - An exclusive advisory lock (`fcntl.flock`) prevents concurrent write races
110 when parallel agents log in simultaneously.
111 - The file is never read or written as part of a repository snapshot.
112
113 ---
114
115 ## Commands
116
117 ### muse auth login
118
119 Store a bearer token in `~/.muse/identity.toml`.
120
121 ```
122 muse auth login [OPTIONS]
123 ```
124
125 **Options:**
126
127 | Flag | Env var | Description |
128 |---|---|---|
129 | `--token TOKEN` | `MUSE_TOKEN` | Bearer token. Reads `MUSE_TOKEN` if not passed explicitly. |
130 | `--hub URL` | — | Hub URL. Falls back to `[hub] url` in `.muse/config.toml`. |
131 | `--name NAME` | — | Display name for this identity. |
132 | `--id ID` | — | Hub-assigned identity ID (stored for reference). |
133 | `--agent` | — | Mark this identity as an agent (default: human). |
134
135 **Resolution order for the token:**
136
137 1. `MUSE_TOKEN` environment variable (preferred — does not appear in shell history)
138 2. `--token` CLI flag (warns about shell history exposure)
139 3. Interactive prompt via `getpass.getpass` (human-only flow)
140
141 **Examples:**
142
143 ```bash
144 # Human — interactive prompt
145 muse auth login --hub https://musehub.ai
146
147 # Agent — non-interactive, token from environment variable
148 MUSE_TOKEN=$MY_SECRET muse auth login \
149 --hub https://musehub.ai \
150 --agent \
151 --name "composer-v2" \
152 --id "agt_xyz"
153
154 # Human — hub URL from repo config (no --hub needed after muse hub connect)
155 muse auth login
156
157 # Override identity metadata after initial login
158 muse auth login --token $MUSE_TOKEN --name "Alice (updated)" --hub musehub.ai
159 ```
160
161 **What login does:**
162
163 1. Resolves the hub URL from `--hub` or the repo's `[hub] url` config.
164 2. Resolves the token from the environment, the flag, or an interactive prompt.
165 3. Warns if the token was passed via the `--token` CLI flag (shell history risk).
166 4. Creates or updates the `[<hostname>]` section in `~/.muse/identity.toml`.
167 5. Sets directory and file permissions (`0o700` / `0o600`).
168
169 ---
170
171 ### muse auth whoami
172
173 Display the stored identity for a hub.
174
175 ```
176 muse auth whoami [OPTIONS]
177 ```
178
179 **Options:**
180
181 | Flag | Description |
182 |---|---|
183 | `--hub URL` | Hub URL to inspect. Defaults to the repo's configured hub. |
184 | `--all` | Show identities for all configured hubs. |
185 | `--json` | Emit JSON instead of human-readable output. |
186
187 The raw token is **never** shown. The output indicates only whether a token is
188 set (`set (Bearer ***)` or `not set`).
189
190 **Examples:**
191
192 ```bash
193 # Human-readable output for the current repo's hub
194 muse auth whoami
195
196 # JSON output — for agent scripts
197 muse auth whoami --json
198
199 # Inspect a specific hub
200 muse auth whoami --hub https://staging.musehub.ai
201
202 # Show all stored identities
203 muse auth whoami --all
204 ```
205
206 **JSON output shape:**
207
208 ```json
209 {
210 "hub": "musehub.ai",
211 "type": "human",
212 "name": "Alice",
213 "id": "usr_abc123",
214 "token_set": "true",
215 "capabilities": []
216 }
217 ```
218
219 **Exit codes:**
220
221 - `0` — identity found and displayed.
222 - Non-zero — no identity stored for the specified hub. Useful in agent scripts:
223
224 ```bash
225 muse auth whoami --hub musehub.ai --json || muse auth login --agent --hub musehub.ai
226 ```
227
228 ---
229
230 ### muse auth logout
231
232 Remove stored credentials for a hub.
233
234 ```
235 muse auth logout [OPTIONS]
236 ```
237
238 **Options:**
239
240 | Flag | Description |
241 |---|---|
242 | `--hub URL` | Hub URL to log out from. Defaults to the repo's configured hub. |
243 | `--all` | Remove credentials for ALL configured hubs. |
244
245 The bearer token is deleted from `~/.muse/identity.toml`. The hub URL in
246 `.muse/config.toml` is preserved — use `muse hub disconnect` to remove the hub
247 association from the repository as well.
248
249 **Examples:**
250
251 ```bash
252 # Log out from the current repo's hub
253 muse auth logout
254
255 # Log out from a specific hub
256 muse auth logout --hub https://staging.musehub.ai
257
258 # Remove all stored credentials
259 muse auth logout --all
260 ```
261
262 ---
263
264 ## Authentication Flows
265
266 ### Human flow (interactive)
267
268 ```bash
269 # 1. Connect the repo to a hub
270 muse hub connect https://musehub.ai
271
272 # 2. Log in (prompts for token)
273 muse auth login
274
275 # 3. Push
276 muse push
277 ```
278
279 ### Agent flow (non-interactive)
280
281 ```bash
282 # In a CI/CD pipeline or autonomous agent:
283 MUSE_TOKEN="$SECRET_FROM_VAULT" muse auth login \
284 --hub https://musehub.ai \
285 --agent \
286 --name "pipeline-agent-$BUILD_ID"
287
288 # Now push without further prompts
289 muse push
290 ```
291
292 ### Checking authentication status in a script
293
294 ```bash
295 if muse auth whoami --json > /dev/null 2>&1; then
296 echo "Authenticated — proceeding with push"
297 muse push
298 else
299 echo "Not authenticated — logging in"
300 MUSE_TOKEN="$SECRET" muse auth login --hub https://musehub.ai --agent
301 fi
302 ```
303
304 ---
305
306 ## Token Security Best Practices
307
308 **Prefer `MUSE_TOKEN` over `--token`.**
309 Tokens passed as `--token` appear in:
310 - Shell history (`~/.zsh_history`, `~/.bash_history`)
311 - Process listings (`ps aux` on Linux)
312 - `/proc/PID/cmdline` on Linux
313
314 `MUSE_TOKEN` does not appear in any of these. Muse warns when a token is
315 passed via the CLI flag:
316
317 ```
318 ⚠️ Token passed via --token flag.
319 It may appear in your shell history and process listings.
320 For automation, prefer: MUSE_TOKEN=<token> muse auth login ...
321 ```
322
323 **Scope tokens to the minimum required capabilities.**
324 For read-only agents, request read-only tokens. For write agents, request
325 only the specific namespaces they need (e.g. `write:midi`).
326
327 **Rotate tokens on schedule.**
328 Re-run `muse auth login` with a new token to overwrite the stored entry. The
329 old token is replaced atomically.
330
331 **Do not share tokens across machines.**
332 Each machine should have its own token. This allows revoking access to a
333 single machine without affecting others.
334
335 ---
336
337 ## Environment Variables
338
339 | Variable | Description |
340 |---|---|
341 | `MUSE_TOKEN` | Bearer token for `muse auth login`. Preferred over `--token`. |
342 | `MUSE_REPO_ROOT` | Override the repository root (used in tests and CI). |
343
344 ---
345
346 *See also:*
347
348 - [`docs/reference/hub.md`](hub.md) — `muse hub connect/status/disconnect/ping`
349 - [`docs/reference/remotes.md`](remotes.md) — `muse push`, `muse fetch`, `muse clone`
350 - [`docs/reference/security.md`](security.md) — security architecture and identity store guarantees