@veris-ai/daytona 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/README.md +127 -0
- package/dist/control-plane.d.ts +89 -0
- package/dist/daytona.d.ts +92 -0
- package/dist/errors.d.ts +56 -0
- package/dist/gateway.d.ts +65 -0
- package/dist/index.cjs +904 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +855 -0
- package/dist/index.js.map +1 -0
- package/dist/network.d.ts +74 -0
- package/dist/receipt.d.ts +36 -0
- package/dist/trust.d.ts +55 -0
- package/dist/veris-api.d.ts +74 -0
- package/dist/version.d.ts +1 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# @veris-ai/daytona
|
|
2
|
+
|
|
3
|
+
Run code in a [Daytona](https://daytona.io) sandbox where calls to
|
|
4
|
+
`api.stripe.com` and the rest of your vendor stack are answered by **Veris
|
|
5
|
+
twins** — stateful, contract-accurate fakes — with the code under test
|
|
6
|
+
completely unmodified.
|
|
7
|
+
|
|
8
|
+
No base-URL overrides, no injected config, no mocking library. Your code keeps
|
|
9
|
+
its production hostnames, credentials and SDKs; the network layer does the rest.
|
|
10
|
+
And every run ends with a **receipt** of what the vendor actually received.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm i @veris-ai/daytona @daytona/sdk
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Both, because `@daytona/sdk` is a peer dependency — that is what keeps
|
|
19
|
+
`err instanceof DaytonaNotFoundError` working across the package boundary.
|
|
20
|
+
|
|
21
|
+
| variable | where from |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `DAYTONA_API_KEY` | [app.daytona.io/dashboard/keys](https://app.daytona.io/dashboard/keys) |
|
|
24
|
+
| `VERIS_API_KEY` | [studio.veris.ai](https://studio.veris.ai) |
|
|
25
|
+
| `VERIS_ENVIRONMENT_ID` | a Veris environment — it decides which vendor services your twin gets |
|
|
26
|
+
|
|
27
|
+
## Use
|
|
28
|
+
|
|
29
|
+
Change one import. Everything else in your Daytona code stays as it was.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { Daytona } from '@veris-ai/daytona' // was '@daytona/sdk'
|
|
33
|
+
|
|
34
|
+
const daytona = new Daytona({ apiKey: process.env.DAYTONA_API_KEY })
|
|
35
|
+
const sbx = await daytona.create({ image: 'python:3.12-slim' })
|
|
36
|
+
|
|
37
|
+
await sbx.process.executeCommand('pytest tests/integration')
|
|
38
|
+
|
|
39
|
+
// The assertion the whole thing exists for: did the twin actually see it?
|
|
40
|
+
await sbx.veris.assertTouched('stripe', { method: 'POST', path: '/v1/charges' })
|
|
41
|
+
|
|
42
|
+
await sbx.delete() // deletes the twin too
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This package re-exports everything from `@daytona/sdk`, so it is the only import
|
|
46
|
+
you need to change. No particular sandbox image is required.
|
|
47
|
+
|
|
48
|
+
### Why `assertTouched` and not just a green suite
|
|
49
|
+
|
|
50
|
+
A test suite that skipped its integration and one that exercised it look
|
|
51
|
+
identical from inside the sandbox. So does a call your code believed it made.
|
|
52
|
+
The receipt is the only thing that separates them, and `assertTouched` throws
|
|
53
|
+
when it is empty.
|
|
54
|
+
|
|
55
|
+
## `sbx.veris`
|
|
56
|
+
|
|
57
|
+
| method | what it gives you |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `receipt()` | every request the twin received, with the mode and integrity of the run |
|
|
60
|
+
| `receipt(service)` | the same for one service |
|
|
61
|
+
| `assertTouched(service, match?)` | throws unless the twin saw matching traffic |
|
|
62
|
+
| `services()` | what the twin answers for, and where |
|
|
63
|
+
| `getDataPlaneEnv()` | `{ DATABASE_URL: … }` for non-HTTP twin services |
|
|
64
|
+
| `getTrustEnv()` | the CA variables injected into the sandbox |
|
|
65
|
+
| `deliverTo(port \| url)` | point vendor webhooks back at your sandbox |
|
|
66
|
+
|
|
67
|
+
`sbx.verisSandboxId` is the twin's id — not to be confused with `sbx.id`, which
|
|
68
|
+
is Daytona's.
|
|
69
|
+
|
|
70
|
+
## Options
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
await daytona.create({
|
|
74
|
+
image: 'node:22',
|
|
75
|
+
veris: {
|
|
76
|
+
egress: 'strict', // default. 'open' sets no allowlist — debugging only
|
|
77
|
+
allowOut: ['internal.corp'],
|
|
78
|
+
allowRegistries: true, // default. npm, PyPI, apt, crates…
|
|
79
|
+
installCa: true, // default
|
|
80
|
+
ttlMinutes: 60,
|
|
81
|
+
attachSandboxId: 'sbx_…', // reuse an existing twin; delete() will not remove it
|
|
82
|
+
disabled: false, // true = a plain Daytona sandbox, no twin
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Coordinates can also come from `veris.apiKey` / `veris.environmentId` /
|
|
88
|
+
`veris.apiBase` instead of the environment.
|
|
89
|
+
|
|
90
|
+
## How it works
|
|
91
|
+
|
|
92
|
+
Every sandbox is created with two Daytona parameters: a `domainAllowList` (the
|
|
93
|
+
vendor hostnames the twin answers for, the gateway, the twin's data planes, and
|
|
94
|
+
package registries — nothing else leaves) and an `outboundProxyUrl` pointing at
|
|
95
|
+
the Veris gateway over HTTP CONNECT.
|
|
96
|
+
|
|
97
|
+
Daytona chains them: sandbox traffic reaches Daytona's own proxy, which drops
|
|
98
|
+
anything not allowlisted and forwards the rest to the gateway, which answers
|
|
99
|
+
vendor hostnames from the twin. Nothing of ours runs inside the sandbox, which
|
|
100
|
+
is why any image works.
|
|
101
|
+
|
|
102
|
+
Before `create()` resolves, a canary probe dials a reserved hostname only the
|
|
103
|
+
gateway answers, whose body carries the twin id. It proves in one request that
|
|
104
|
+
egress is tunnelled, that the credential reached the right twin, and that trust
|
|
105
|
+
is wired — and it cannot pass by accident, because outside the tunnel that host
|
|
106
|
+
has no listener. It runs again on every `receipt()`, so a receipt is never
|
|
107
|
+
reported from a sandbox whose egress cannot be vouched for.
|
|
108
|
+
|
|
109
|
+
## Errors
|
|
110
|
+
|
|
111
|
+
Every error is a `VerisError` with a `phase`, so a failure says which of the
|
|
112
|
+
four systems involved refused:
|
|
113
|
+
|
|
114
|
+
`credentials` · `twin-provision` · `credential-mint` · `sandbox-create` ·
|
|
115
|
+
`ca-install` · `canary` · `receipt` · `attach`
|
|
116
|
+
|
|
117
|
+
## Limitations
|
|
118
|
+
|
|
119
|
+
- **Requires a Veris control plane that serves an HTTP CONNECT gateway.**
|
|
120
|
+
Daytona accepts only `http`/`https` outbound proxies, so a SOCKS-only gateway
|
|
121
|
+
cannot be used; `create()` then fails at `credential-mint` saying so.
|
|
122
|
+
- **QUIC/HTTP3 and ECH are not intercepted.** The gateway relays TCP. Both are
|
|
123
|
+
reported in the receipt's `leaks` rather than silently omitted.
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
Apache-2.0. Source: [veris-ai/veris-daytona](https://github.com/veris-ai/veris-daytona).
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export interface RouteEntry {
|
|
2
|
+
/** A real vendor hostname the service answers for. */
|
|
3
|
+
host: string;
|
|
4
|
+
/** Path prefixes narrowing the claim when several services share the host. */
|
|
5
|
+
paths?: string[] | null;
|
|
6
|
+
}
|
|
7
|
+
export interface ServiceInfo {
|
|
8
|
+
name: string;
|
|
9
|
+
status: string;
|
|
10
|
+
/** What the code under test points at: gateway URL for http services, a DSN for e.g. postgres. */
|
|
11
|
+
url: string;
|
|
12
|
+
/** Where /veris/* lives — always an http URL. */
|
|
13
|
+
control_url: string;
|
|
14
|
+
env_hint?: string | null;
|
|
15
|
+
routes?: RouteEntry[] | null;
|
|
16
|
+
}
|
|
17
|
+
export interface TwinSandbox {
|
|
18
|
+
id: string;
|
|
19
|
+
environment_id: string;
|
|
20
|
+
status: 'provisioning' | 'ready' | 'failed' | 'degraded' | 'terminating' | string;
|
|
21
|
+
created_at?: string | null;
|
|
22
|
+
expires_at?: string | null;
|
|
23
|
+
services: ServiceInfo[];
|
|
24
|
+
failure_reason?: string | null;
|
|
25
|
+
metadata?: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
/** Response of POST /v1/sandboxes/{sid}/egress-credential (gateway mode). */
|
|
28
|
+
export interface EgressCredential {
|
|
29
|
+
/** SOCKS5 endpoint. What @veris-ai/e2b uses; Daytona cannot (it accepts only
|
|
30
|
+
* http/https outbound proxies). */
|
|
31
|
+
socks_address: string;
|
|
32
|
+
/** HTTP CONNECT endpoint, host:port. Absent on a control plane that predates
|
|
33
|
+
* the CONNECT listener — which is a hard error here, since Daytona has no
|
|
34
|
+
* other way to reach the gateway. */
|
|
35
|
+
connect_address?: string;
|
|
36
|
+
/** The CONNECT endpoint as a complete proxy URL, credentials included.
|
|
37
|
+
* Preferred over building one from connect_address: the gateway owns its own
|
|
38
|
+
* auth format, and guessing it is how you end up sending the wrong password. */
|
|
39
|
+
http_proxy_url?: string;
|
|
40
|
+
username: string;
|
|
41
|
+
password: string;
|
|
42
|
+
ca_pem: string;
|
|
43
|
+
canary_host: string;
|
|
44
|
+
min_sdk?: string;
|
|
45
|
+
expires_at?: string;
|
|
46
|
+
/** Server-served CA trust env map; the SDK's vendored list is the fallback. */
|
|
47
|
+
trust_env?: Record<string, string>;
|
|
48
|
+
}
|
|
49
|
+
/** Mutable fields of a running twin. An OMITTED key is left alone; an explicit
|
|
50
|
+
* null is a value (client_base_url: null unregisters). */
|
|
51
|
+
export interface SandboxPatch {
|
|
52
|
+
ttl_minutes?: number;
|
|
53
|
+
client_base_url?: string | null;
|
|
54
|
+
}
|
|
55
|
+
export interface ControlPlaneOpts {
|
|
56
|
+
apiKey: string;
|
|
57
|
+
apiBase: string;
|
|
58
|
+
/** Sent as X-Veris-SDK on every request, so the server can version-gate. */
|
|
59
|
+
sdkVersion: string;
|
|
60
|
+
}
|
|
61
|
+
export declare class ControlPlane {
|
|
62
|
+
readonly apiBase: string;
|
|
63
|
+
private readonly headers;
|
|
64
|
+
constructor(opts: ControlPlaneOpts);
|
|
65
|
+
private request;
|
|
66
|
+
private json;
|
|
67
|
+
createTwin(environmentId: string, opts?: {
|
|
68
|
+
ttlMinutes?: number;
|
|
69
|
+
metadata?: Record<string, string>;
|
|
70
|
+
}): Promise<TwinSandbox>;
|
|
71
|
+
getTwin(sandboxId: string): Promise<TwinSandbox | null>;
|
|
72
|
+
/** Poll until the twin reports ready. "failed" is terminal per the API docs. */
|
|
73
|
+
waitReady(sandboxId: string, timeoutMs: number): Promise<TwinSandbox>;
|
|
74
|
+
services(sandboxId: string): Promise<ServiceInfo[]>;
|
|
75
|
+
deleteTwin(environmentId: string, sandboxId: string): Promise<boolean>;
|
|
76
|
+
/**
|
|
77
|
+
* Mint (or re-mint) the gateway egress credential for a twin. Returns null
|
|
78
|
+
* when the control plane does not offer gateway mode at all (404 — route
|
|
79
|
+
* absent), so `mode: 'auto'` can fall back; throws VerisGatewayNotOfferedError
|
|
80
|
+
* on an explicit version refusal (409 sdk_too_old).
|
|
81
|
+
*/
|
|
82
|
+
mintEgressCredential(environmentId: string, sandboxId: string): Promise<EgressCredential | null>;
|
|
83
|
+
/** PATCH the twin resource. Omitted fields are untouched by the server. */
|
|
84
|
+
updateSandbox(environmentId: string, sandboxId: string, patch: SandboxPatch): Promise<void>;
|
|
85
|
+
/** Extend a twin's TTL so it stays in lockstep with an extended Daytona sandbox. */
|
|
86
|
+
extendTtl(environmentId: string, sandboxId: string, ttlMinutes: number): Promise<void>;
|
|
87
|
+
/** Create-time preflight: is the gateway infrastructure up, per the control plane? */
|
|
88
|
+
gatewayHealth(): Promise<void>;
|
|
89
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Daytona as BaseDaytona } from '@daytona/sdk';
|
|
2
|
+
import type { CreateSandboxFromImageParams, CreateSandboxFromSnapshotParams, DaytonaConfig, ListSandboxesQuery, Sandbox } from '@daytona/sdk';
|
|
3
|
+
import type { VerisApi } from './veris-api';
|
|
4
|
+
import type { EgressMode } from './network';
|
|
5
|
+
import { CA_CERT_PATH } from './trust';
|
|
6
|
+
export interface VerisOpts {
|
|
7
|
+
/** Veris API key. Falls back to process.env.VERIS_API_KEY. Required. */
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
/** Veris environment the twin is deployed from. Falls back to process.env.VERIS_ENVIRONMENT_ID. */
|
|
10
|
+
environmentId?: string;
|
|
11
|
+
/** Control plane base. Falls back to process.env.VERIS_API_BASE, then 'https://svc.api.veris.ai'. */
|
|
12
|
+
apiBase?: string;
|
|
13
|
+
/** Attach to an EXISTING twin instead of provisioning one (advanced). delete() will NOT remove it. */
|
|
14
|
+
attachSandboxId?: string;
|
|
15
|
+
/** Twin TTL backstop, minutes. Default 60, kept in step with the sandbox's ttlMinutes. */
|
|
16
|
+
ttlMinutes?: number;
|
|
17
|
+
/** 'strict' (default): the sandbox reaches only its twin, its data planes,
|
|
18
|
+
* the control plane and package registries. 'open': no allowlist at all —
|
|
19
|
+
* debugging only, because a bypassing client then reaches the real vendor. */
|
|
20
|
+
egress?: EgressMode;
|
|
21
|
+
/** Extra hostnames to allow out. */
|
|
22
|
+
allowOut?: string[];
|
|
23
|
+
/** Allow package registries (npm, PyPI, apt, …). Default true: a coding
|
|
24
|
+
* sandbox that cannot install dependencies is not usable. */
|
|
25
|
+
allowRegistries?: boolean;
|
|
26
|
+
/** Install the gateway CA into the sandbox trust store. Default true; without
|
|
27
|
+
* it every HTTPS call to a vendor host fails certificate validation. */
|
|
28
|
+
installCa?: boolean;
|
|
29
|
+
/** Inject { [env_hint]: dsn } for non-HTTP twin services. Default true. */
|
|
30
|
+
dataPlaneEnv?: boolean;
|
|
31
|
+
/** Turn Veris off entirely for this create — a plain Daytona sandbox. */
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface VerisDaytonaConfig extends DaytonaConfig {
|
|
35
|
+
veris?: VerisOpts;
|
|
36
|
+
}
|
|
37
|
+
/** A Daytona sandbox with the Veris surface attached. */
|
|
38
|
+
export type VerisSandbox = Sandbox & {
|
|
39
|
+
/** Receipts, services, assertions. Everything Veris adds. */
|
|
40
|
+
veris: VerisApi;
|
|
41
|
+
/** The Veris twin's id. Not to be confused with `sandbox.id`, the Daytona one. */
|
|
42
|
+
verisSandboxId: string;
|
|
43
|
+
};
|
|
44
|
+
/** Is this sandbox one of ours? Narrows for callers who hold a bare Sandbox. */
|
|
45
|
+
export declare function isVerisSandbox(sbx: Sandbox): sbx is VerisSandbox;
|
|
46
|
+
export declare class Daytona extends BaseDaytona {
|
|
47
|
+
private readonly verisDefaults;
|
|
48
|
+
constructor(config?: VerisDaytonaConfig);
|
|
49
|
+
create(params?: CreateSandboxFromSnapshotParams & {
|
|
50
|
+
veris?: VerisOpts;
|
|
51
|
+
}, options?: {
|
|
52
|
+
timeout?: number;
|
|
53
|
+
}): Promise<Sandbox>;
|
|
54
|
+
create(params?: CreateSandboxFromImageParams & {
|
|
55
|
+
veris?: VerisOpts;
|
|
56
|
+
}, options?: {
|
|
57
|
+
onSnapshotCreateLogs?: (chunk: string) => void;
|
|
58
|
+
timeout?: number;
|
|
59
|
+
}): Promise<Sandbox>;
|
|
60
|
+
/**
|
|
61
|
+
* Rehydrate the Veris surface on an existing sandbox.
|
|
62
|
+
*
|
|
63
|
+
* Not an optimisation — a necessity. The OpenCode plugin reconnects to a
|
|
64
|
+
* sandbox with get() on every resumed session and deletes through get() too,
|
|
65
|
+
* so a get() that returned a bare Sandbox would mean no receipts after any
|
|
66
|
+
* restart and a leaked twin on every delete.
|
|
67
|
+
*/
|
|
68
|
+
get(sandboxIdOrName: string): Promise<Sandbox>;
|
|
69
|
+
/** Same rehydration for the sandboxes a list() streams. */
|
|
70
|
+
list(query?: ListSandboxesQuery): AsyncIterableIterator<Sandbox>;
|
|
71
|
+
/**
|
|
72
|
+
* Attach the Veris surface to a sandbox whose labels say it has a twin.
|
|
73
|
+
* A sandbox without our labels is passed through untouched — callers can use
|
|
74
|
+
* this client for ordinary Daytona work.
|
|
75
|
+
*/
|
|
76
|
+
private rehydrate;
|
|
77
|
+
private provisionTwin;
|
|
78
|
+
/**
|
|
79
|
+
* Hang the Veris surface off the instance, and wrap delete() so teardown is
|
|
80
|
+
* automatic.
|
|
81
|
+
*
|
|
82
|
+
* Wrapping rather than asking callers to remember is the point: the OpenCode
|
|
83
|
+
* plugin's existing `sandbox.delete()` then removes the twin too, with no
|
|
84
|
+
* change to the plugin at all. A twin outlives its sandbox otherwise, until
|
|
85
|
+
* its TTL reaps it — invisible until the bill arrives.
|
|
86
|
+
*/
|
|
87
|
+
private attach;
|
|
88
|
+
/** super.create through the overload the params actually match. */
|
|
89
|
+
private baseCreate;
|
|
90
|
+
}
|
|
91
|
+
export default Daytona;
|
|
92
|
+
export { CA_CERT_PATH };
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every failure phase a Veris error can name. Bring-up spans four systems
|
|
3
|
+
* (control plane, Daytona API, the sandbox, the Veris gateway), so errors
|
|
4
|
+
* carry a structured phase rather than a wall of logs.
|
|
5
|
+
*/
|
|
6
|
+
export type VerisErrorPhase = 'credentials' | 'twin-provision' | 'gateway-preflight' | 'credential-mint' | 'sandbox-create' | 'ca-install' | 'canary' | 'receipt' | 'attach';
|
|
7
|
+
/**
|
|
8
|
+
* Base class for every error this package throws. Deliberately NOT a subclass
|
|
9
|
+
* of any Daytona error: `e instanceof VerisError` cleanly separates Veris
|
|
10
|
+
* failures from Daytona failures in one catch.
|
|
11
|
+
*/
|
|
12
|
+
export declare class VerisError extends Error {
|
|
13
|
+
readonly phase?: VerisErrorPhase;
|
|
14
|
+
/** The Veris twin's sandbox id, when one exists yet. */
|
|
15
|
+
readonly verisSandboxId?: string;
|
|
16
|
+
/** Verbatim control-plane response body, when the failure came from an API call. */
|
|
17
|
+
readonly responseBody?: unknown;
|
|
18
|
+
constructor(message: string, opts?: {
|
|
19
|
+
phase?: VerisErrorPhase;
|
|
20
|
+
verisSandboxId?: string;
|
|
21
|
+
responseBody?: unknown;
|
|
22
|
+
cause?: unknown;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/** A required credential/coordinate is missing. Thrown before any network call, naming the exact variable. */
|
|
26
|
+
export declare class MissingCredentialsError extends VerisError {
|
|
27
|
+
}
|
|
28
|
+
/** The Veris gateway infrastructure is down (control-plane health said so). */
|
|
29
|
+
export declare class VerisGatewayUnreachableError extends VerisError {
|
|
30
|
+
}
|
|
31
|
+
/** The control plane does not offer gateway mode (endpoint absent, or this SDK version is below min_sdk). */
|
|
32
|
+
export declare class VerisGatewayNotOfferedError extends VerisError {
|
|
33
|
+
/** Server-announced minimum SDK version, when the refusal carried one. */
|
|
34
|
+
readonly minSdk?: string;
|
|
35
|
+
constructor(message: string, opts?: ConstructorParameters<typeof VerisError>[1] & {
|
|
36
|
+
minSdk?: string;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/** The canary probe failed: egress is not (or no longer) reaching the twin. */
|
|
40
|
+
export declare class ReceiptIntegrityError extends VerisError {
|
|
41
|
+
}
|
|
42
|
+
/** assertTouched(): the named service saw zero intercepted requests — green without the receipt. */
|
|
43
|
+
export declare class VerisUntouchedError extends VerisError {
|
|
44
|
+
readonly service: string;
|
|
45
|
+
constructor(message: string, service: string, opts?: ConstructorParameters<typeof VerisError>[1]);
|
|
46
|
+
}
|
|
47
|
+
/** The Daytona sandbox is alive but its Veris twin is gone (TTL expiry, delete, reset). */
|
|
48
|
+
export declare class TwinExpiredError extends VerisError {
|
|
49
|
+
}
|
|
50
|
+
/** The sandbox image cannot host the Veris layer — in practice, no
|
|
51
|
+
* ca-certificates, so the gateway CA cannot be trusted. */
|
|
52
|
+
export declare class SnapshotUnsupportedError extends VerisError {
|
|
53
|
+
}
|
|
54
|
+
/** An inherited Daytona operation that would break the one-sandbox-one-twin invariant (e.g. fork). */
|
|
55
|
+
export declare class UnsupportedOperationError extends VerisError {
|
|
56
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Sandbox } from '@daytona/sdk';
|
|
2
|
+
/** Single-quote a string for POSIX sh. */
|
|
3
|
+
export declare function shellQuote(s: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* Build the proxy URL Daytona is handed.
|
|
6
|
+
*
|
|
7
|
+
* The username is the tenant demux key — the gateway reads the sandbox id out
|
|
8
|
+
* of it. There is no separate secret: the id IS the capability, exactly as the
|
|
9
|
+
* SOCKS path treats it, so the password is a placeholder the gateway ignores.
|
|
10
|
+
*
|
|
11
|
+
* RFC 3986 §3.2.1 notes that `user:password` in userinfo is deprecated. It is
|
|
12
|
+
* also the only form HTTP proxy clients accept for RFC 7617 Basic credentials,
|
|
13
|
+
* and it is what Daytona forwards, so it is what we emit.
|
|
14
|
+
*
|
|
15
|
+
* `encodeURIComponent` is exactly right for userinfo: everything it leaves raw
|
|
16
|
+
* is unreserved or a sub-delim, and it escapes both separators — `:` to %3A and
|
|
17
|
+
* `@` to %40 — so a username can never break out into the authority.
|
|
18
|
+
*
|
|
19
|
+
* The address is validated rather than interpolated: it arrives from the
|
|
20
|
+
* control plane, and an unchecked value here would land in a URL and then in
|
|
21
|
+
* every client's proxy configuration.
|
|
22
|
+
*/
|
|
23
|
+
export declare function gatewayProxyUrl(credential: {
|
|
24
|
+
http_proxy_url?: string;
|
|
25
|
+
connect_address?: string;
|
|
26
|
+
username: string;
|
|
27
|
+
}): string;
|
|
28
|
+
/**
|
|
29
|
+
* Make the gateway's CA trusted, without requiring anything of the image.
|
|
30
|
+
*
|
|
31
|
+
* Defensive rather than load-bearing on Daytona today, and the distinction is
|
|
32
|
+
* worth recording. Daytona's own proxy terminates TLS with a certificate signed
|
|
33
|
+
* by ITS CA — already trusted in the image — and re-originates to the gateway,
|
|
34
|
+
* so the client never validates our forged leaf. Verified: a vendor call
|
|
35
|
+
* succeeds with --cacert naming only Daytona's CA.
|
|
36
|
+
*
|
|
37
|
+
* We install ours regardless, because the day Daytona tunnels CONNECT
|
|
38
|
+
* end-to-end (the ordinary behaviour for an HTTP proxy) the gateway's leaf
|
|
39
|
+
* reaches the client directly and nothing works without it. One upload and one
|
|
40
|
+
* shell command against a total outage is a trade worth making.
|
|
41
|
+
*
|
|
42
|
+
* The obvious approach — drop the cert in /usr/local/share/ca-certificates and
|
|
43
|
+
* run update-ca-certificates — needs root AND that tool, and Daytona's default
|
|
44
|
+
* image has neither. So the artefact is a bundle we build ourselves at a
|
|
45
|
+
* world-writable path: the distribution's roots (when it has any) plus ours.
|
|
46
|
+
*
|
|
47
|
+
* Daytona overrides the best-known trust variables with its own CA, correctly
|
|
48
|
+
* for its proxy. The dozen it does not set still point at this bundle, which
|
|
49
|
+
* carries both CAs and every public root — so those tools verify rather than
|
|
50
|
+
* break.
|
|
51
|
+
*
|
|
52
|
+
* The system-store install still runs when it can, for anything that reads the
|
|
53
|
+
* store directly rather than honouring the variables. It is best-effort.
|
|
54
|
+
*/
|
|
55
|
+
export declare function installCa(sandbox: Sandbox, caPem: string): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* The canary probe: one HTTPS request from inside the sandbox to a reserved
|
|
58
|
+
* hostname only the gateway answers, with the twin id in the body.
|
|
59
|
+
*
|
|
60
|
+
* Green proves three things in a single request — egress really is tunnelled
|
|
61
|
+
* through the gateway, the credential demuxed to the right twin, and the CA
|
|
62
|
+
* install worked. Dialled outside the tunnel the host has no listener, so this
|
|
63
|
+
* cannot pass by accident, which is what makes a receipt worth reading.
|
|
64
|
+
*/
|
|
65
|
+
export declare function probeCanary(sandbox: Sandbox, canaryHost: string, expectedTwinId: string): Promise<void>;
|