@veris-ai/daytona 0.2.1 → 0.3.1

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/dist/trust.d.ts CHANGED
@@ -36,6 +36,144 @@ export declare const VERIS_BUNDLE = "/tmp/veris-ca-bundle.crt";
36
36
  * is the one file that works in both worlds.
37
37
  */
38
38
  export declare function vendoredTrustEnv(): Record<string, string>;
39
+ /**
40
+ * The same variables as one POSIX `export` prelude, to prefix a command with.
41
+ *
42
+ * The map above is the right shape when you control the process's environment.
43
+ * A caller that does not — anything handing a command to a shell whose env it
44
+ * did not build — has nowhere to put a map, and the command inherits Daytona's
45
+ * overwritten SSL_CERT_FILE / REQUESTS_CA_BUNDLE / CURL_CA_BUNDLE /
46
+ * NODE_EXTRA_CA_CERTS instead. Measured with the inherited value: `uv sync`
47
+ * dies with "invalid peer certificate: UnknownIssuer". Prefixing this makes
48
+ * the command's own shell put the Veris bundle back:
49
+ *
50
+ * sandbox.process.executeCommand(`${trustPrelude()} ${yourCommand}`)
51
+ *
52
+ * Values are single-quoted, so the string stays one safe command line whatever
53
+ * the control plane served.
54
+ */
55
+ export declare function trustPrelude(env?: Record<string, string>): string;
56
+ /**
57
+ * CA bundles that SDKs ship INSIDE the code under test, as slash-anchored path
58
+ * suffixes.
59
+ *
60
+ * No trust variable reaches these. stripe-python passes
61
+ * `verify=stripe.ca_bundle_path` explicitly, so all eighteen variables above
62
+ * are irrelevant to it and the first Stripe call dies with "Could not verify
63
+ * Stripe's SSL certificate" — the gateway's forged leaf is signed by a CA that
64
+ * file has never heard of. Appending our CA to the file keeps the SDK on the
65
+ * code path that ships; the bundle merely holds one more root.
66
+ *
67
+ * The table is the veris CLI's (internal/bundlescan), for the reason it gives
68
+ * there: matched as a path SUFFIX because site-packages prefixes vary per
69
+ * image and the tail does not, and deliberately with no bare `cacert.pem`
70
+ * rule, because that filename also names test fixtures and client-auth
71
+ * material that must not quietly gain a root.
72
+ */
73
+ export declare const BUNDLED_CA_FILES: readonly {
74
+ sdk: string;
75
+ suffix: string;
76
+ }[];
77
+ /** Where the patch script is written inside the sandbox, so anything that can
78
+ * run a shell command — a test harness, an agent, a person over ssh — can
79
+ * re-run it after installing dependencies without holding this SDK. */
80
+ export declare const BUNDLED_CA_PATCH_SCRIPT = "/tmp/veris-patch-bundled-cas.sh";
81
+ /**
82
+ * What the script prints per patched file, so the caller can report what
83
+ * happened rather than infer it from an exit code.
84
+ *
85
+ * A sentence rather than a machine marker, because both routes to this script
86
+ * are read by a person. `provision` tells a caller to run `sh
87
+ * /tmp/veris-patch-bundled-cas.sh` themselves, and whatever the script prints
88
+ * is what they see — a bare `__VERIS_PATCHED__ /path` reads as debug output
89
+ * that escaped. `patchBundledCas()` parses the paths back off this same
90
+ * constant, and cli.ts prints the same sentence for the SDK route, so the
91
+ * prose a human reads and the list a program gets cannot drift apart.
92
+ */
93
+ export declare const BUNDLED_CA_PATCHED_MARKER = "the Veris CA was appended to ";
94
+ /**
95
+ * The script that finds those bundles and appends the Veris CA to each.
96
+ *
97
+ * Meant to run AFTER dependencies are installed, and to be re-run: the bundles
98
+ * do not exist at create time, and an agent installs more of them mid-session,
99
+ * so there is no create-time moment that covers either. Idempotent by
100
+ * construction — a file already carrying our certificate is skipped, matched
101
+ * on a line of its base64 body rather than on the BEGIN line every certificate
102
+ * shares.
103
+ *
104
+ * Appending in place, where the CLI over-mounts a patched copy: a Daytona
105
+ * sandbox offers no bind mounts and the container is disposable, so editing
106
+ * the file is the equivalent move. Unwritable files are skipped rather than
107
+ * sudo'd — a root-owned bundle in the system Python is rarely the one the
108
+ * application's virtualenv reads, and silently rewriting system trust is a
109
+ * larger act than this warrants.
110
+ */
111
+ export declare function bundledCaPatchScript(): string;
112
+ /**
113
+ * Node is the one runtime the trust variables above cannot reach.
114
+ *
115
+ * Daytona overwrites NODE_EXTRA_CA_CERTS and SSL_CERT_FILE inside the sandbox
116
+ * with its own CA file — right for clients that go through its proxy, which
117
+ * terminates TLS with that CA. Node is not such a client: it ignores
118
+ * HTTPS_PROXY, is forwarded to the gateway end to end, and the leaf it
119
+ * validates is signed by the Veris CA, which Daytona's file lacks. Seen live:
120
+ * plain `node` failed against a vendor host with UNABLE_TO_VERIFY_LEAF_SIGNATURE.
121
+ */
122
+ /**
123
+ * Node's trust flag. NODE_EXTRA_CA_CERTS is Daytona's, and Node's baked-in
124
+ * Mozilla roots know neither CA — so switch Node to OpenSSL's store:
125
+ * --use-openssl-ca loads SSL_CERT_FILE (Daytona's file) AND the system
126
+ * certificate directory, where CA_INSTALL_CMD puts our CA and the distribution
127
+ * keeps the public roots. Injected as NODE_OPTIONS, which Daytona leaves alone.
128
+ *
129
+ * Verified live on Daytona's default image (uid 1001, passwordless sudo,
130
+ * update-ca-certificates present): 200 from plain `node` with the flag and
131
+ * our CA in /etc/ssl/certs; UNABLE_TO_VERIFY_LEAF_SIGNATURE without the flag.
132
+ * So this leans on the store install succeeding. There is no second route:
133
+ * Daytona's CA file is on a read-only mount, so appending our CA to it — the
134
+ * obvious alternative — fails for root too.
135
+ */
136
+ export declare const NODE_TRUST_FLAG = "--use-openssl-ca";
137
+ /** NODE_OPTIONS with the trust flag appended to whatever the caller set. */
138
+ export declare function nodeOptionsWithTrust(existing?: string): string;
139
+ /**
140
+ * Where the proxy preload lands in the sandbox. World-readable, like the CA
141
+ * bundle: anything that starts a Node process can name it.
142
+ */
143
+ export declare const NODE_PROXY_PRELOAD = "/tmp/veris-node-proxy.cjs";
144
+ /** The NODE_OPTIONS flag that loads it before the application's first line. */
145
+ export declare const NODE_PROXY_FLAG = "--require /tmp/veris-node-proxy.cjs";
146
+ /**
147
+ * NODE_USE_ENV_PROXY reaches exactly two things: Node's global agents and the
148
+ * global fetch dispatcher. An SDK that constructs its own http(s).Agent — the
149
+ * usual way to get keep-alive pooling; stripe-node, the AWS SDK's Node handler
150
+ * and Twilio's client all do — never sees the proxy, resolves the vendor host
151
+ * itself, and dies on Daytona's blocked egress with EAI_AGAIN. Measured with
152
+ * stripe-node 15 in a wired sandbox: the global agent 200, its own agent
153
+ * EAI_AGAIN, its own agent with `proxyEnv: process.env` 200.
154
+ *
155
+ * So every Agent is given the proxy environment at construction, from a
156
+ * preload Node loads before the application: subclass the two Agent classes
157
+ * and default `proxyEnv` to the process environment. `proxyEnv` is Node 24+;
158
+ * older Node ignores the option, which is also where NODE_USE_ENV_PROXY is
159
+ * inert, so nothing regresses there. A caller's own `proxyEnv` wins because
160
+ * the spread comes after ours. Clients built on undici Pools or Clients hold
161
+ * their own dispatcher and are not covered here.
162
+ */
163
+ export declare function nodeProxyPreloadScript(): string;
164
+ /** NODE_OPTIONS with the proxy preload appended to whatever the caller set. */
165
+ export declare function nodeOptionsWithProxy(existing?: string): string;
166
+ /**
167
+ * The NODE_OPTIONS a wired sandbox needs, merged into the caller's own: the
168
+ * proxy preload always, the trust flag when the CA is installed. Appended,
169
+ * never replaced, and never twice — a caller's `--max-old-space-size` or
170
+ * `--experimental-vm-modules` keeps working beside ours. Use it wherever a
171
+ * NODE_OPTIONS value is built for a command in the sandbox, so setting one
172
+ * for the application cannot silently drop what makes its vendor calls work.
173
+ */
174
+ export declare function verisNodeOptions(existing?: string, opts?: {
175
+ trust?: boolean;
176
+ }): string;
39
177
  /**
40
178
  * The store-based install, for stacks that read a trust store rather than an
41
179
  * env var — a Java client honours no CA variable at all. Rebuilds the system
@@ -1,3 +1,5 @@
1
+ import type { ReceiptBaseline } from './run-receipt';
2
+ import type { ControlOptions, ControlResource } from './service-control';
1
3
  import type { Sandbox } from '@daytona/sdk';
2
4
  import type { ControlPlane, ServiceInfo } from './control-plane';
3
5
  import type { Receipt, ReceiptEntry } from './receipt';
@@ -13,6 +15,13 @@ export interface VerisContext {
13
15
  canaryHost: string;
14
16
  /** Whether this twin is owned (delete removes it) or attached (caller owns it). */
15
17
  ownsTwin: boolean;
18
+ /** Newest request id per service when this run began, so a receipt counts
19
+ * what the run caused rather than what an attached twin already held. Set by
20
+ * create(); a sandbox rehydrated by get() has none and reads the log from
21
+ * the start, as the receipt always did. Not persisted in a label on
22
+ * purpose — the sandbox can rewrite its own labels, and a watermark it
23
+ * chose would let it hide its own traffic from the receipt. */
24
+ watermarks?: Record<string, number>;
16
25
  }
17
26
  /** Narrow assertTouched to specific requests. All fields AND together. */
18
27
  export interface TouchMatcher {
@@ -23,17 +32,32 @@ export interface TouchMatcher {
23
32
  minRequests?: number;
24
33
  }
25
34
  export interface VerisApi {
35
+ /** Whether provider teardown owns this twin. */
36
+ readonly ownsTwin: boolean;
26
37
  /** The Veris twin's sandbox id — NOT the Daytona sandbox id. */
27
38
  readonly sandboxId: string;
39
+ /** The Veris environment the twin was deployed from. Every control-plane
40
+ * route that acts on a twin is scoped to it, so a caller that wants to talk
41
+ * to the twin itself needs it and has nowhere else to read it from. */
42
+ readonly environmentId: string;
28
43
  readonly mode: 'gateway';
29
44
  services(): Promise<ServiceInfo[]>;
30
45
  /** The service's own manual: what it models and how its data is shaped. */
31
46
  manual(service: string): Promise<string>;
47
+ receiptBaseline(): Promise<ReceiptBaseline>;
48
+ receiptSince(baseline: ReceiptBaseline, service?: string): Promise<Receipt>;
49
+ control(service: string, resource: ControlResource, options?: ControlOptions): Promise<unknown>;
32
50
  receipt(): Promise<Receipt>;
33
51
  receipt(service: string): Promise<ReceiptEntry>;
34
52
  assertTouched(service: string, match?: TouchMatcher): Promise<void>;
35
53
  getDataPlaneEnv(): Promise<Record<string, string>>;
36
54
  getTrustEnv(): Record<string, string>;
55
+ /** The same trust variables as a shell `export` prelude, for a caller that
56
+ * can only prefix a command line. */
57
+ trustPrelude(): string;
58
+ /** Append the Veris CA to the CA bundles SDKs ship with them. Run it after
59
+ * installing dependencies; returns the files it changed. */
60
+ patchBundledCas(): Promise<string[]>;
37
61
  deliverTo(port: number, opts?: DeliverToOpts): Promise<string>;
38
62
  deliverTo(url: string | null, opts?: DeliverToOpts): Promise<string | null>;
39
63
  }
@@ -45,7 +69,9 @@ export interface DeliverToOpts {
45
69
  export declare class VerisApiImpl implements VerisApi {
46
70
  private readonly ctx;
47
71
  constructor(ctx: VerisContext);
72
+ get ownsTwin(): boolean;
48
73
  get sandboxId(): string;
74
+ get environmentId(): string;
49
75
  get mode(): 'gateway';
50
76
  services(): Promise<ServiceInfo[]>;
51
77
  /** Resolve a service by name. A typo and a service that exists but saw
@@ -60,10 +86,42 @@ export declare class VerisApiImpl implements VerisApi {
60
86
  * ride around it — named rather than rounded off.
61
87
  */
62
88
  private leaks;
89
+ /** Where this service's log was when the run began. 0 reads all of it. */
90
+ private watermark;
91
+ private verifyIntegrity;
92
+ receiptBaseline(): Promise<ReceiptBaseline>;
93
+ receiptSince(baseline: ReceiptBaseline, service?: string): Promise<Receipt>;
94
+ control(service: string, resource: ControlResource, options?: ControlOptions): Promise<unknown>;
63
95
  assertTouched(service: string, match?: TouchMatcher): Promise<void>;
64
96
  getDataPlaneEnv(): Promise<Record<string, string>>;
65
97
  /** The CA trust vars injected at create, for callers building their own env. */
66
98
  getTrustEnv(): Record<string, string>;
99
+ /**
100
+ * The same variables as a shell prelude, for a caller that has no env map to
101
+ * fill — anything running a command through a session or a shell it did not
102
+ * build the environment for. Daytona resets SSL_CERT_FILE,
103
+ * REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE and NODE_EXTRA_CA_CERTS to its own CA
104
+ * file inside the sandbox, and that file cannot verify the gateway's leaf, so
105
+ * a command that inherits them fails on certificate validation:
106
+ * `uv sync` dies with "invalid peer certificate: UnknownIssuer".
107
+ *
108
+ * sbx.process.executeCommand(`${sbx.veris.trustPrelude()} uv sync`)
109
+ */
110
+ trustPrelude(): string;
111
+ /**
112
+ * Append the Veris CA to the CA bundles the code under test's own SDKs ship.
113
+ *
114
+ * The variables above reach every client that reads one. stripe-python does
115
+ * not: it passes `verify=stripe.ca_bundle_path`, so the first Stripe call
116
+ * fails with "Could not verify Stripe's SSL certificate" in a sandbox where
117
+ * curl, Node and `requests` all succeed. This patches the file itself, which
118
+ * is the Daytona-shaped version of the veris CLI's --patch-bundled-cas.
119
+ *
120
+ * Call it AFTER installing dependencies — the bundles arrive with them, so
121
+ * there is nothing to patch at create time. Safe to call again; it returns
122
+ * only the files it actually changed.
123
+ */
124
+ patchBundledCas(): Promise<string[]>;
67
125
  /**
68
126
  * Point every mocked vendor's callbacks/webhooks at this sandbox.
69
127
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veris-ai/daytona",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "Veris twin interception for Daytona: a drop-in @daytona/sdk whose sandboxes come up with a Veris twin already answering their vendor API calls. Unmodified code, real hostnames, receipts.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -13,6 +13,9 @@
13
13
  "require": "./dist/index.cjs"
14
14
  }
15
15
  },
16
+ "bin": {
17
+ "veris-daytona": "./dist/cli.js"
18
+ },
16
19
  "sideEffects": false,
17
20
  "files": [
18
21
  "dist",
@@ -28,6 +31,9 @@
28
31
  "prepublishOnly": "npm run build",
29
32
  "dev": "tsup --watch"
30
33
  },
34
+ "dependencies": {
35
+ "yaml": "^2.9.0"
36
+ },
31
37
  "peerDependencies": {
32
38
  "@daytona/sdk": ">=0.204.1 <1"
33
39
  },