@geostack/arc 0.1.0
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/LICENSE +21 -0
- package/README.md +122 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +890 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +632 -0
- package/dist/index.js +1514 -0
- package/dist/index.js.map +1 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GEOstack
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# @geostack/arc
|
|
2
|
+
|
|
3
|
+
Arc makes high-risk AI actions safe with permissions, approvals, signed execution, and audit.
|
|
4
|
+
|
|
5
|
+
Arc does not run your app logic. Your app owns the action implementation. Arc verifies authority, asks for approval when policy requires it, signs the execution request, delivers it to your app, and records the audit trail.
|
|
6
|
+
|
|
7
|
+
## Define Actions
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { arc } from "@geostack/arc";
|
|
11
|
+
|
|
12
|
+
export const actions = arc.defineActions({
|
|
13
|
+
issue_refund: {
|
|
14
|
+
name: "Issue refund",
|
|
15
|
+
risk: "high",
|
|
16
|
+
defaultDecision: "ask",
|
|
17
|
+
input: {
|
|
18
|
+
type: "object",
|
|
19
|
+
required: ["amount", "customerId"],
|
|
20
|
+
properties: {
|
|
21
|
+
amount: { type: "number" },
|
|
22
|
+
customerId: { type: "string" }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`risk` must be `low`, `medium`, or `high`. `defaultDecision` must be `allow`, `ask`, or `block`.
|
|
30
|
+
|
|
31
|
+
## Handle Signed Execution
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import express from "express";
|
|
35
|
+
import { arc } from "@geostack/arc";
|
|
36
|
+
import { actions } from "./actions.js";
|
|
37
|
+
import { nonceStore } from "./arc-nonce-store.js";
|
|
38
|
+
|
|
39
|
+
const app = express();
|
|
40
|
+
app.use(express.json());
|
|
41
|
+
|
|
42
|
+
app.post("/arc/execute", arc.handleAction(actions, {
|
|
43
|
+
issue_refund: async ({ input, appUserId, invocationId }) => {
|
|
44
|
+
// Enforce app-side idempotency by invocationId before side effects.
|
|
45
|
+
// Store the in-progress/completed record in durable storage shared by all app instances.
|
|
46
|
+
if (await refundAlreadyHandled(invocationId)) {
|
|
47
|
+
return getStoredRefundResult(invocationId);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return issueRefund(appUserId, input);
|
|
51
|
+
}
|
|
52
|
+
}, {
|
|
53
|
+
apiUrl: "http://localhost:4000",
|
|
54
|
+
nonceStore
|
|
55
|
+
}));
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`handleAction()` verifies Arc's ES256 JWS, body hash, timestamp freshness, nonce replay hook, and signature claims before dispatching your handler.
|
|
59
|
+
|
|
60
|
+
For lower-level integrations, call `verifyArcRequest(req, { jwks, nonceStore })` directly. The nonce store must return `false` for replayed nonces. Verification fails with `nonce_store_required` when no store is supplied.
|
|
61
|
+
|
|
62
|
+
For production, use durable nonce replay storage shared by every app instance. The SDK's memory nonce store is a local development helper only and must be enabled explicitly with `unsafeAllowInMemoryNonceStore`. A production app that verifies without durable nonce storage is not safely integrated with Arc.
|
|
63
|
+
|
|
64
|
+
Your app should also treat `invocation_id` as an idempotency key before performing side effects, because Arc may retry delivery after network failures or 5xx responses. A robust handler writes an idempotency row before the side effect and stores the final result when the side effect completes. If the process crashes after the app side effect but before Arc records success, Arc will mark the invocation outcome unknown rather than blindly retrying; your app's idempotency record is the source of truth for reconciliation.
|
|
65
|
+
|
|
66
|
+
Minimal nonce store contract:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
export const nonceStore = {
|
|
70
|
+
async useNonce(nonce: string, expiresAt: Date) {
|
|
71
|
+
// Atomically insert nonce with TTL. Return false when it already exists.
|
|
72
|
+
const result = await redis.set(`arc:nonce:${nonce}`, "1", {
|
|
73
|
+
NX: true,
|
|
74
|
+
PXAT: expiresAt.getTime()
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return result === "OK";
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Sync Actions
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
arc config set --api-url http://localhost:4000
|
|
86
|
+
arc app create "Refund App" --execute-url http://host.docker.internal:8787/arc/execute
|
|
87
|
+
arc actions sync ./src/actions.ts
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The CLI stores local configuration in `~/.arc/config.json`. It never stores app API keys. Dev agent tokens are only stored when you create them with `arc agent dev-token`, and the CLI labels them as local development credentials.
|
|
91
|
+
|
|
92
|
+
## Invoke Locally
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
arc agent dev-token
|
|
96
|
+
arc invoke issue_refund --app refund-app --input '{"amount":480,"customerId":"cus_123"}'
|
|
97
|
+
arc approvals list
|
|
98
|
+
arc approvals approve <approval_id>
|
|
99
|
+
arc audit tail
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Allowed invocations are queued for signed delivery. Asked invocations create approvals. Blocked invocations never execute.
|
|
103
|
+
|
|
104
|
+
## HTTP Clients
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { ArcDeveloperClient, ArcAgentClient } from "@geostack/arc";
|
|
108
|
+
|
|
109
|
+
const developer = new ArcDeveloperClient({ baseUrl: "http://localhost:4000" });
|
|
110
|
+
await developer.devLogin({ email: "dev@example.test" });
|
|
111
|
+
|
|
112
|
+
const agent = new ArcAgentClient({
|
|
113
|
+
baseUrl: "http://localhost:4000",
|
|
114
|
+
agentToken: "arc_agent_..."
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The clients return Arc API JSON directly and throw `ArcHttpError` with `status`, `code`, and `message` for non-2xx responses.
|
|
119
|
+
|
|
120
|
+
## Production Notes
|
|
121
|
+
|
|
122
|
+
This SDK is V1 developer tooling, not a claim that the whole Arc stack is ready for public production traffic. Before public launch, run Arc with a production signing key, persistent app-side nonce storage, app-side idempotency by `invocation_id`, observability around failed/unknown executions, and network egress controls. The local Docker stack and committed development signing key are for local development only.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
type CliEnv = NodeJS.ProcessEnv;
|
|
3
|
+
type CliIo = {
|
|
4
|
+
stderr: Pick<NodeJS.WriteStream, "write">;
|
|
5
|
+
stdout: Pick<NodeJS.WriteStream, "write">;
|
|
6
|
+
};
|
|
7
|
+
export declare function main(argv?: string[], env?: CliEnv, io?: CliIo): Promise<number>;
|
|
8
|
+
export {};
|