Skip to content

Commit 2bfd00f

Browse files
authored
fix(proxy): use project default account instead of hash-based selection (#157)
* fix(proxy): use project default account instead of hash-based selection Changes account selection logic to follow proper priority: 1. MSL-Account header (if provided) 2. Project's default_account_id from database 3. Error if no default account configured Removed hash-based deterministic account selection as it was not the intended behavior. Projects should explicitly configure their default account via the dashboard. **Changes:** - Modified getProjectCredentials() to return only the project's default account - Simplified AuthenticationService.authenticate() to remove hash logic - Removed unused rankCredentials() method and crypto import - Updated tests to match new behavior - Updated documentation (ADR-024, API reference, configuration guide, README) **Breaking Change:** Projects that relied on hash-based account selection will now need to have a default account configured. Use the dashboard to set the default account for each project. Refs: #157 * test(proxy): fix Slack configuration test for new account selection logic
1 parent 2fa4880 commit 2bfd00f

File tree

8 files changed

+169
-423
lines changed

8 files changed

+169
-423
lines changed

IMPORTANT_FILES.yaml

Lines changed: 0 additions & 191 deletions
This file was deleted.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,13 +340,13 @@ See the [Documentation](docs/README.md) for complete configuration options.
340340
```bash
341341
curl -X POST http://localhost:3000/v1/messages \
342342
-H "MSL-Project-Id: project-alpha" \
343-
-H "MSL-Account: account-primary" \
343+
-H "MSL-Account: acc_abc123xyz" \
344344
-H "Authorization: Bearer cnp_live_team_alpha" \
345345
-H "Content-Type: application/json" \
346346
-d '{"model":"claude-3-opus-20240229","messages":[{"role":"user","content":"Hello"}]}'
347347
```
348348
349-
If `MSL-Account` is omitted, the proxy deterministically maps each project to an available account (hash-based), falling back to others only if the primary account is unavailable.
349+
If `MSL-Account` is omitted, the proxy uses the project's configured default account. Each project must have a default account set via the dashboard.
350350
351351
## Usage
352352

docs/01-Getting-Started/configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ Proxy access tokens live under `credentials/project-client-keys/<project-id>.cli
7676
```
7777

7878
- `MSL-Project-Id` header selects the project and unlocks analytics.
79-
- `MSL-Account` chooses the account credential file; when omitted, the proxy deterministically maps the project to an account using a stable hash.
79+
- `MSL-Account` chooses the account credential by account ID; when omitted, the proxy uses the project's configured default account.
8080

8181
## Database Configuration
8282

docs/02-User-Guide/api-reference.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,15 @@ Use the `MSL-Project-Id` header to associate requests with a project/train, and
2626

2727
```bash
2828
MSL-Project-Id: project-alpha
29-
MSL-Account: account-primary # optional
29+
MSL-Account: acc_abc123xyz # optional - account ID to override project default
3030
```
3131

32+
**Account Selection Priority:**
33+
34+
1. If `MSL-Account` header is present, use the specified account ID
35+
2. Otherwise, use the project's configured default account
36+
3. If no default account is configured, return an error
37+
3238
## Endpoints
3339

3440
### Messages API

docs/04-Architecture/ADRs/adr-024-train-id-header-routing.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ credentials** are used to fulfil it (account).
2323

2424
1. **Explicit Headers**
2525
- `MSL-Project-Id` remains mandatory for analytics but now defaults to `DEFAULT_PROJECT_ID` when absent.
26-
- `MSL-Account` selects the Anthropic account credential file. When omitted the proxy chooses a
27-
deterministic account based on the project identifier, falling back to other accounts only when the
28-
preferred one fails to load.
26+
- `MSL-Account` selects the Anthropic account credential by account ID. When omitted, the proxy uses the
27+
project's configured default account. If no default account is configured, an error is returned.
2928
- Neither header is forwarded to Anthropic; only configured custom headers (e.g. via
3029
`ANTHROPIC_CUSTOM_HEADERS`) are propagated.
3130

@@ -45,11 +44,11 @@ credentials** are used to fulfil it (account).
4544
### Positive
4645

4746
- Clear separation between projects (analytics) and accounts (credentials).
48-
- Simpler operational model: new projects require only a header; new accounts require only a credential
49-
file.
47+
- Simpler operational model: new projects require only a header and default account configuration; new
48+
accounts require only a credential entry in the database.
5049
- Eliminates wildcard precedence edge cases and large path traversal surface area.
51-
- Deterministic hashing provides consistent per-project account selection while still distributing load
52-
across multiple keys.
50+
- Project default accounts provide consistent account selection per project while allowing override via
51+
`MSL-Account` header.
5352

5453
### Negative
5554

packages/shared/src/database/queries/project-queries.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,15 +225,21 @@ export async function setProjectDefaultAccount(
225225
}
226226

227227
/**
228-
* Get all credentials available to a project (all credentials)
228+
* Get the default credential for a project
229+
* Returns only the project's default_account_id credential
229230
*/
230231
export async function getProjectCredentials(
231232
pool: Pool,
232-
_projectId: string
233+
projectId: string
233234
): Promise<AnthropicCredential[]> {
234-
// All projects have access to all credentials
235235
const result = await pool.query<AnthropicCredential>(
236-
`SELECT * FROM anthropic_credentials ORDER BY account_name ASC`
236+
`
237+
SELECT ac.*
238+
FROM projects p
239+
JOIN anthropic_credentials ac ON p.default_account_id = ac.id
240+
WHERE p.project_id = $1
241+
`,
242+
[projectId]
237243
)
238244

239245
return result.rows

0 commit comments

Comments
 (0)