@@ -505,12 +505,122 @@ credentials:
505505| -------------------------- | ------------------------------------------------------------- |
506506| `sources` | Map of service identifier to credential source. |
507507| `sources.<id>.env` | Environment variables to read on the host, in priority order. |
508- | `sources.<id>.file.path` | Path on host. `~` expands to home. |
509- | `sources.<id>.file.parser` | How to extract the value (for example, `"json:apiKey"`). |
508+ | `sources.<id>.file.path` | Path on host. `~` expands to home directory. |
509+ | `sources.<id>.file.parser` | How to extract the credential value from the file. |
510510| `sources.<id>.priority` | `env-first` (default) or `file-first`. |
511511
512512Service identifiers link credentials to [network rules](#network).
513513
514+ # ### file.parser
515+
516+ ` file.parser` controls how the proxy extracts a credential value from the file at
517+ `file.path`. Three forms are supported :
518+
519+ | Parser value | Behavior |
520+ | ----------------- | ------------------------------------------------------------------------------------ |
521+ | omitted or empty | Reads the entire file as the credential. Leading and trailing whitespace is trimmed. |
522+ | `json:<dot.path>` | Parses the file as JSON and extracts the value at the given dot-separated path. |
523+ | anything else | Rejected with `unsupported parser : <value>`. |
524+
525+ ` json:` is the only supported structured-file format.
526+
527+ **JSON path rules**
528+
529+ - Path segments are separated by `.` — for example, `json:credentials.github.token`.
530+ - Only JSON objects can be navigated. Arrays are not supported; there is no `[0]`-style indexing.
531+ - Keys that contain a literal `.` cannot be referenced — the dot is always treated as a separator.
532+ - The value at the resolved path must be a string, number, or boolean. Numbers and booleans are
533+ converted to strings. Objects, arrays, and null are rejected with
534+ ` field '<path>' is not a string value` .
535+
536+ **Priority and missing files**
537+
538+ When a source has both `env` and `file`, `priority` controls which is tried first. If the
539+ preferred source is unavailable (environment variable unset, or file missing or inaccessible),
540+ the proxy falls back to the other source automatically. A missing file is not an error on its
541+ own — the source is silently skipped.
542+
543+ **Examples**
544+
545+ Plain-text token file — no parser needed :
546+
547+ ` ` ` yaml
548+ credentials:
549+ sources:
550+ openai:
551+ file:
552+ path: "~/.openai/token"
553+ ` ` `
554+
555+ Top-level JSON field :
556+
557+ ` ` ` yaml
558+ credentials:
559+ sources:
560+ anthropic:
561+ file:
562+ path: "~/.claude/settings.json"
563+ parser: "json:primaryApiKey"
564+ ` ` `
565+
566+ Given `~/.claude/settings.json` :
567+
568+ ` ` ` json
569+ { "primaryApiKey": "sk-ant-...", "secondaryApiKey": "sk-ant-..." }
570+ ` ` `
571+
572+ The proxy resolves the credential to the value of `primaryApiKey`.
573+
574+ Nested JSON field :
575+
576+ ` ` ` yaml
577+ credentials:
578+ sources:
579+ github:
580+ file:
581+ path: "~/.config/myapp/creds.json"
582+ parser: "json:credentials.github.token"
583+ ` ` `
584+
585+ Given `~/.config/myapp/creds.json` :
586+
587+ ` ` ` json
588+ {
589+ "credentials": {
590+ "github": { "token": "ghp_xyz", "expires": "2026-12-31" }
591+ }
592+ }
593+ ` ` `
594+
595+ The proxy resolves the credential to `ghp_xyz`.
596+
597+ File-first with env fallback :
598+
599+ ` ` ` yaml
600+ credentials:
601+ sources:
602+ anthropic:
603+ env:
604+ - ANTHROPIC_API_KEY
605+ file:
606+ path: "~/.claude/settings.json"
607+ parser: "json:primaryApiKey"
608+ priority: file-first
609+ ` ` `
610+
611+ Tries the JSON file first; falls back to the environment variable if the file is
612+ missing or the field is absent.
613+
614+ **Common errors**
615+
616+ | Error message | Cause |
617+ | --------------------------------------------- | ------------------------------------------------------------------- |
618+ | `field 'X' not found in JSON` | The path doesn't exist in the file. |
619+ | `cannot navigate to field 'X' : not an object` | A path segment hit a string, array, or scalar instead of an object. |
620+ | `field 'X' is not a string value` | The resolved value is an object, array, or null. |
621+ | `failed to parse JSON : ...` | The file is not valid JSON. |
622+ | `unsupported parser : <value>` | The parser string doesn't start with `json:` and isn't empty. |
623+
514624# ## Network
515625
516626` ` ` yaml
0 commit comments