the universal sandbox runtime for agents and humans. pocketenv.io
sandbox openclaw agent claude-code vercel-sandbox deno-sandbox cloudflare-sandbox atproto sprites daytona
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add provider token handling for Daytona and Deno

CLI now encrypts and redacts provider tokens (DENO_DEPLOY_TOKEN,
DAYTONA_API_KEY)
and includes them in the createSandbox payload; added a redact util. API
lexicons,
types and PKL updated with new fields. Sandbox validation enforces
provider-
specific required fields and the server decrypts tokens on creation.

+182 -2
+20
apps/api/lexicons/sandbox/createSandbox.json
··· 90 90 "redactedSpriteToken": { 91 91 "type": "string", 92 92 "description": "A redacted token for accessing sprite resources" 93 + }, 94 + "denoDeployToken": { 95 + "type": "string", 96 + "description": "A token (encrypted) for accessing Deno Deploy resources" 97 + }, 98 + "redactedDenoDeployToken": { 99 + "type": "string", 100 + "description": "A redacted token for accessing Deno Deploy resources" 101 + }, 102 + "daytonaApiKey": { 103 + "type": "string", 104 + "description": "A token (encrypted) for accessing Daytona resources" 105 + }, 106 + "redactedDaytonaApiKey": { 107 + "type": "string", 108 + "description": "A redacted token for accessing Daytona resources" 109 + }, 110 + "daytonaOrganizationId": { 111 + "type": "string", 112 + "description": "The organization ID for Daytona resources" 93 113 } 94 114 } 95 115 }
+20
apps/api/pkl/defs/sandbox/createSandbox.pkl
··· 88 88 type = "string" 89 89 description = "A redacted token for accessing sprite resources" 90 90 } 91 + ["denoDeployToken"] = new StringType { 92 + type = "string" 93 + description = "A token (encrypted) for accessing Deno Deploy resources" 94 + } 95 + ["redactedDenoDeployToken"] = new StringType { 96 + type = "string" 97 + description = "A redacted token for accessing Deno Deploy resources" 98 + } 99 + ["daytonaApiKey"] = new StringType { 100 + type = "string" 101 + description = "A token (encrypted) for accessing Daytona resources" 102 + } 103 + ["redactedDaytonaApiKey"] = new StringType { 104 + type = "string" 105 + description = "A redacted token for accessing Daytona resources" 106 + } 107 + ["daytonaOrganizationId"] = new StringType { 108 + type = "string" 109 + description = "The organization ID for Daytona resources" 110 + } 91 111 } 92 112 } 93 113 }
+23
apps/api/src/lexicon/lexicons.ts
··· 595 595 type: "string", 596 596 description: "A redacted token for accessing sprite resources", 597 597 }, 598 + denoDeployToken: { 599 + type: "string", 600 + description: 601 + "A token (encrypted) for accessing Deno Deploy resources", 602 + }, 603 + redactedDenoDeployToken: { 604 + type: "string", 605 + description: 606 + "A redacted token for accessing Deno Deploy resources", 607 + }, 608 + daytonaApiKey: { 609 + type: "string", 610 + description: 611 + "A token (encrypted) for accessing Daytona resources", 612 + }, 613 + redactedDaytonaApiKey: { 614 + type: "string", 615 + description: "A redacted token for accessing Daytona resources", 616 + }, 617 + daytonaOrganizationId: { 618 + type: "string", 619 + description: "The organization ID for Daytona resources", 620 + }, 598 621 }, 599 622 }, 600 623 },
+10
apps/api/src/lexicon/types/io/pocketenv/sandbox/createSandbox.ts
··· 40 40 spriteToken?: string; 41 41 /** A redacted token for accessing sprite resources */ 42 42 redactedSpriteToken?: string; 43 + /** A token (encrypted) for accessing Deno Deploy resources */ 44 + denoDeployToken?: string; 45 + /** A redacted token for accessing Deno Deploy resources */ 46 + redactedDenoDeployToken?: string; 47 + /** A token (encrypted) for accessing Daytona resources */ 48 + daytonaApiKey?: string; 49 + /** A redacted token for accessing Daytona resources */ 50 + redactedDaytonaApiKey?: string; 51 + /** The organization ID for Daytona resources */ 52 + daytonaOrganizationId?: string; 43 53 [k: string]: unknown; 44 54 } 45 55
+5
apps/api/src/xrpc/io/pocketenv/sandbox/createSandbox.ts
··· 111 111 keepAlive: input.keepAlive, 112 112 spriteToken: input.spriteToken, 113 113 redactedSpriteToken: input.redactedSpriteToken, 114 + denoDeployToken: input.denoDeployToken, 115 + redactedDenoDeployToken: input.redactedDenoDeployToken, 116 + daytonaApiKey: input.daytonaApiKey, 117 + redactedDaytonaApiKey: input.redactedDaytonaApiKey, 118 + daytonaOrganizationId: input.daytonaOrganizationId, 114 119 }, 115 120 { 116 121 headers: {
+50 -2
apps/cli/src/cmd/create.ts
··· 6 6 import { c } from "../theme"; 7 7 import { expandRepo } from "../lib/expandRepo"; 8 8 import waitUntilRunning from "../lib/waitUntilRunning"; 9 + import encrypt from "../lib/sodium"; 10 + import redact from "../lib/redact"; 9 11 10 12 async function createSandbox( 11 13 name: string, ··· 24 26 const token = await getAccessToken(); 25 27 if (repo) repo = expandRepo(repo); 26 28 27 - if (["deno", "vercel", "daytona"].includes(provider || "")) { 29 + const providerOptions: Record<string, any> = {}; 30 + 31 + if ( 32 + !["sprites", "daytona", "deno", "vercel", "cloudflare"].includes( 33 + provider ?? "cloudflare", 34 + ) 35 + ) { 28 36 consola.error( 29 - `This Sandbox Runtime is temporarily disabled. ${c.primary(provider ?? "")}`, 37 + `Unsupported provider: ${provider}. Supported providers are: sprites, daytona, deno, vercel, cloudflare (default).`, 30 38 ); 31 39 process.exit(1); 32 40 } 41 + 42 + if (provider === "sprites") { 43 + const spriteToken = process.env.SPRITE_TOKEN; 44 + if (!spriteToken) { 45 + consola.error( 46 + "SPRITE_TOKEN environment variable is required for Sprites provider.", 47 + ); 48 + process.exit(1); 49 + } 50 + providerOptions.spriteToken = await encrypt(spriteToken); 51 + providerOptions.redactedSpriteToken = redact(spriteToken); 52 + } 53 + 54 + if (provider === "daytona") { 55 + const daytonaApiKey = process.env.DAYTONA_API_KEY; 56 + const daytonaOrganizationId = process.env.DAYTONA_ORGANIZATION_ID; 57 + if (!daytonaApiKey || !daytonaOrganizationId) { 58 + consola.error( 59 + "DAYTONA_API_KEY and DAYTONA_ORGANIZATION_ID environment variables are required for Daytona provider.", 60 + ); 61 + process.exit(1); 62 + } 63 + providerOptions.daytonaApiKey = await encrypt(daytonaApiKey); 64 + providerOptions.redactedDaytonaApiKey = redact(daytonaApiKey); 65 + providerOptions.daytonaOrganizationId = daytonaOrganizationId; 66 + } 67 + 68 + if (provider === "deno") { 69 + const denoDeployToken = process.env.DENO_DEPLOY_TOKEN; 70 + if (!denoDeployToken) { 71 + consola.error( 72 + "DENO_DEPLOY_TOKEN environment variable is required for Deno provider.", 73 + ); 74 + process.exit(1); 75 + } 76 + providerOptions.denoDeployToken = await encrypt(denoDeployToken); 77 + providerOptions.redactedDenoDeployToken = redact(denoDeployToken); 78 + } 79 + 33 80 try { 34 81 const sandbox = await client.post<Sandbox>( 35 82 "/xrpc/io.pocketenv.sandbox.createSandbox", ··· 40 87 "at://did:plc:aturpi2ls3yvsmhc6wybomun/io.pocketenv.sandbox/openclaw", 41 88 provider: provider ?? "cloudflare", 42 89 repo, 90 + ...providerOptions, 43 91 }, 44 92 { 45 93 headers: {
+9
apps/cli/src/lib/redact.ts
··· 1 + export default function redact(value: string) { 2 + if (value.length <= 14) { 3 + return value; 4 + } 5 + const visibleStart = value.slice(0, 11); 6 + const visibleEnd = value.slice(-3); 7 + const redactedMiddle = "*".repeat(24); 8 + return `${visibleStart}${redactedMiddle}${visibleEnd}`; 9 + }
+1
apps/sandbox/src/index.ts
··· 202 202 spriteName, 203 203 daytonaApiKey: decrypt(params.daytonaApiKey), 204 204 organizationId: params.daytonaOrganizationId, 205 + denoDeployToken: decrypt(params.denoDeployToken), 205 206 }); 206 207 const sandboxId = await sandbox.id(); 207 208
+44
apps/sandbox/src/types/sandbox.ts
··· 96 96 }); 97 97 } 98 98 } 99 + 100 + if (data.provider === "daytona") { 101 + if (!data.daytonaApiKey) { 102 + ctx.addIssue({ 103 + code: z.ZodIssueCode.custom, 104 + message: "daytonaApiKey is required when provider is 'daytona'", 105 + path: ["daytonaApiKey"], 106 + }); 107 + } 108 + if (!data.redactedDaytonaApiKey) { 109 + ctx.addIssue({ 110 + code: z.ZodIssueCode.custom, 111 + message: 112 + "redactedDaytonaApiKey is required when provider is 'daytona'", 113 + path: ["redactedDaytonaApiKey"], 114 + }); 115 + } 116 + if (!data.daytonaOrganizationId) { 117 + ctx.addIssue({ 118 + code: z.ZodIssueCode.custom, 119 + message: 120 + "daytonaOrganizationId is required when provider is 'daytona'", 121 + path: ["daytonaOrganizationId"], 122 + }); 123 + } 124 + } 125 + 126 + if (data.provider === "deno") { 127 + if (!data.denoDeployToken) { 128 + ctx.addIssue({ 129 + code: z.ZodIssueCode.custom, 130 + message: "denoDeployToken is required when provider is 'deno'", 131 + path: ["denoDeployToken"], 132 + }); 133 + } 134 + if (!data.redactedDenoDeployToken) { 135 + ctx.addIssue({ 136 + code: z.ZodIssueCode.custom, 137 + message: 138 + "redactedDenoDeployToken is required when provider is 'deno'", 139 + path: ["redactedDenoDeployToken"], 140 + }); 141 + } 142 + } 99 143 }); 100 144 101 145 export const StartSandboxInputSchema = z.object({