axvault 1.9.2 → 1.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -192,7 +192,7 @@ This command requires `--force` or `--yes` to confirm.
|
|
|
192
192
|
|
|
193
193
|
### Container Deployments
|
|
194
194
|
|
|
195
|
-
Container images are published automatically to `registry.j4k.dev/axvault` on every release (multi-arch: amd64 + arm64).
|
|
195
|
+
Container images are published automatically to `registry.j4k.dev/axvault` on every release (multi-arch: amd64 + arm64). To rebuild manually, run `workflow_dispatch` on `publish-image` and provide the required `version` input (for example `1.7.0`).
|
|
196
196
|
|
|
197
197
|
#### Running the Container
|
|
198
198
|
|
|
@@ -392,14 +392,14 @@ When `X-Axvault-Refresh-Failed` is present, the response still returns HTTP 200
|
|
|
392
392
|
|
|
393
393
|
### With axrun (Recommended)
|
|
394
394
|
|
|
395
|
-
Use the `--vault-credential` flag to fetch credentials directly:
|
|
395
|
+
Use the `--vault-credential` flag to fetch credentials directly. Match the credential name to the agent (for example `ci-claude-oauth-token` for Claude and `ci-codex-oauth-credentials` for Codex):
|
|
396
396
|
|
|
397
397
|
```yaml
|
|
398
398
|
- name: Run Claude Review
|
|
399
399
|
env:
|
|
400
400
|
AXVAULT: ${{ secrets.AXVAULT }}
|
|
401
401
|
run: |
|
|
402
|
-
axrun --agent claude --vault-credential ci-oauth-token \
|
|
402
|
+
axrun --agent claude --vault-credential ci-claude-oauth-token \
|
|
403
403
|
--prompt "Review this PR"
|
|
404
404
|
```
|
|
405
405
|
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
* Encapsulates refresh decision logic + per-credential mutex so the handler
|
|
5
5
|
* stays small enough for static complexity checks (FTA).
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import { isValidAgentCli, isRefreshableCredentialType, parseCredentials, } from "axshared";
|
|
7
|
+
import { refreshBlob } from "axauth";
|
|
9
8
|
import { getCredential, updateCredentialIfUnchanged, } from "../db/repositories/credentials.js";
|
|
10
9
|
import { logAccess } from "../db/repositories/audit-log.js";
|
|
11
10
|
import { decryptCredential, encryptCredential } from "../lib/encryption.js";
|
|
11
|
+
import { shouldRefreshCredential } from "./should-refresh-credential.js";
|
|
12
12
|
/** Per-credential mutex to prevent concurrent refreshes */
|
|
13
13
|
const pendingRefreshes = new Map();
|
|
14
14
|
function getRefreshPromise(name, blob, agent, provider, refreshTimeoutMs) {
|
|
@@ -29,31 +29,8 @@ function getRefreshPromise(name, blob, agent, provider, refreshTimeoutMs) {
|
|
|
29
29
|
return promise;
|
|
30
30
|
}
|
|
31
31
|
async function refreshCredentialOnRead(options) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
status: "ok",
|
|
35
|
-
blob: options.blob,
|
|
36
|
-
updatedAt: options.expectedUpdatedAt,
|
|
37
|
-
wasRefreshed: false,
|
|
38
|
-
refreshFailed: false,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
// Refresh requires a valid agent ID (empty means pre-v2 credential)
|
|
42
|
-
if (!isValidAgentCli(options.agent)) {
|
|
43
|
-
return {
|
|
44
|
-
status: "ok",
|
|
45
|
-
blob: options.blob,
|
|
46
|
-
updatedAt: options.expectedUpdatedAt,
|
|
47
|
-
wasRefreshed: false,
|
|
48
|
-
refreshFailed: false,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
const agent = options.agent;
|
|
52
|
-
const parsedCredentials = parseCredentials(options.blob);
|
|
53
|
-
if (parsedCredentials === undefined ||
|
|
54
|
-
!isRefreshableCredentialType(parsedCredentials.type) ||
|
|
55
|
-
!isRefreshable(parsedCredentials.data) ||
|
|
56
|
-
isCredentialExpired(parsedCredentials.data, options.refreshThresholdSeconds) !== true) {
|
|
32
|
+
const decision = shouldRefreshCredential(options.blob, options.agent, options.refreshThresholdSeconds);
|
|
33
|
+
if (!decision.refresh) {
|
|
57
34
|
return {
|
|
58
35
|
status: "ok",
|
|
59
36
|
blob: options.blob,
|
|
@@ -63,7 +40,7 @@ async function refreshCredentialOnRead(options) {
|
|
|
63
40
|
};
|
|
64
41
|
}
|
|
65
42
|
try {
|
|
66
|
-
const refreshResult = await getRefreshPromise(options.name, options.blob, agent, options.provider, options.refreshTimeoutMs);
|
|
43
|
+
const refreshResult = await getRefreshPromise(options.name, options.blob, decision.agent, options.provider, options.refreshTimeoutMs);
|
|
67
44
|
if (refreshResult.ok) {
|
|
68
45
|
const encrypted = encryptCredential(refreshResult.blob);
|
|
69
46
|
const updateResult = updateCredentialIfUnchanged(options.database, {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision logic for whether a credential should be refreshed on read.
|
|
3
|
+
*
|
|
4
|
+
* Extracted to keep refresh-credential-on-read.ts under the FTA threshold.
|
|
5
|
+
*/
|
|
6
|
+
import { type AgentCli } from "axshared";
|
|
7
|
+
/** Check whether a credential should be refreshed */
|
|
8
|
+
declare function shouldRefreshCredential(blob: unknown, agent: string, refreshThresholdSeconds: number): {
|
|
9
|
+
refresh: true;
|
|
10
|
+
agent: AgentCli;
|
|
11
|
+
} | {
|
|
12
|
+
refresh: false;
|
|
13
|
+
};
|
|
14
|
+
export { shouldRefreshCredential };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision logic for whether a credential should be refreshed on read.
|
|
3
|
+
*
|
|
4
|
+
* Extracted to keep refresh-credential-on-read.ts under the FTA threshold.
|
|
5
|
+
*/
|
|
6
|
+
import { isCredentialExpired, isRefreshable } from "axauth";
|
|
7
|
+
import { isValidAgentCli, isRefreshableCredentialType, parseCredentials, } from "axshared";
|
|
8
|
+
/** Check whether a credential should be refreshed */
|
|
9
|
+
function shouldRefreshCredential(blob, agent, refreshThresholdSeconds) {
|
|
10
|
+
if (refreshThresholdSeconds <= 0)
|
|
11
|
+
return { refresh: false };
|
|
12
|
+
if (!isValidAgentCli(agent))
|
|
13
|
+
return { refresh: false };
|
|
14
|
+
const parsed = parseCredentials(blob);
|
|
15
|
+
if (parsed === undefined ||
|
|
16
|
+
!isRefreshableCredentialType(parsed.type) ||
|
|
17
|
+
!isRefreshable(parsed.data) ||
|
|
18
|
+
isCredentialExpired(parsed.data, refreshThresholdSeconds) !== true) {
|
|
19
|
+
return { refresh: false };
|
|
20
|
+
}
|
|
21
|
+
return { refresh: true, agent };
|
|
22
|
+
}
|
|
23
|
+
export { shouldRefreshCredential };
|