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). Use `workflow_dispatch` on the `publish-image` workflow to rebuild manually.
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 { isCredentialExpired, isRefreshable, refreshBlob, } from "axauth";
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
- if (options.refreshThresholdSeconds <= 0) {
33
- return {
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 };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "axvault",
3
3
  "author": "Łukasz Jerciński",
4
4
  "license": "MIT",
5
- "version": "1.9.2",
5
+ "version": "1.9.4",
6
6
  "description": "Remote credential storage server for axkit",
7
7
  "repository": {
8
8
  "type": "git",