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.

Verify sandbox ownership in key endpoints

Ensure the authenticated user owns the sandbox before modifying SSH or
Tailscale keys. Join key, sandboxes, and users tables to check the
user's DID and return 404 if no matching sandbox is found. Add required
imports and use an AND predicate in the queries.

+38 -2
+18 -1
apps/api/src/xrpc/io/pocketenv/sandbox/putSshKeys.ts
··· 1 1 import { XRPCError, type HandlerAuth } from "@atproto/xrpc-server"; 2 2 import type { Context } from "context"; 3 - import { eq } from "drizzle-orm"; 3 + import { and, eq } from "drizzle-orm"; 4 4 import type { Server } from "lexicon"; 5 5 import type { InputSchema } from "lexicon/types/io/pocketenv/sandbox/putSshKeys"; 6 + import sandboxes from "schema/sandboxes"; 6 7 import sshKeys from "schema/ssh-keys"; 8 + import users from "schema/users"; 7 9 8 10 export default function (server: Server, ctx: Context) { 9 11 const putSshKeys = async (input: InputSchema, auth: HandlerAuth) => { ··· 12 14 } 13 15 14 16 await ctx.db.transaction(async (tx) => { 17 + const sandbox = await tx 18 + .select() 19 + .from(sshKeys) 20 + .leftJoin(sandboxes, eq(sshKeys.sandboxId, sandboxes.id)) 21 + .leftJoin(users, eq(sandboxes.userId, users.id)) 22 + .where( 23 + and( 24 + eq(sshKeys.sandboxId, input.id), 25 + eq(users.did, auth.credentials.did), 26 + ), 27 + ) 28 + .execute(); 29 + if (sandbox.length === 0) { 30 + throw new XRPCError(404, "Sandbox not found"); 31 + } 15 32 await tx.delete(sshKeys).where(eq(sshKeys.sandboxId, input.id)).execute(); 16 33 await tx 17 34 .insert(sshKeys)
+20 -1
apps/api/src/xrpc/io/pocketenv/sandbox/putTailscaleAuthKey.ts
··· 1 1 import { XRPCError, type HandlerAuth } from "@atproto/xrpc-server"; 2 2 import type { Context } from "context"; 3 - import { eq } from "drizzle-orm"; 3 + import { and, eq } from "drizzle-orm"; 4 4 import type { Server } from "lexicon"; 5 5 import type { InputSchema } from "lexicon/types/io/pocketenv/sandbox/putTailscaleAuthKey"; 6 + import sandboxes from "schema/sandboxes"; 6 7 import tailscaleAuthKeys from "schema/tailscale-auth-keys"; 8 + import users from "schema/users"; 7 9 8 10 export default function (server: Server, ctx: Context) { 9 11 const putTailscaleAuthKey = async (input: InputSchema, auth: HandlerAuth) => { ··· 12 14 } 13 15 14 16 await ctx.db.transaction(async (tx) => { 17 + const sandbox = await tx 18 + .select() 19 + .from(tailscaleAuthKeys) 20 + .leftJoin(sandboxes, eq(tailscaleAuthKeys.sandboxId, sandboxes.id)) 21 + .leftJoin(users, eq(sandboxes.userId, users.id)) 22 + .where( 23 + and( 24 + eq(tailscaleAuthKeys.sandboxId, input.id), 25 + eq(users.did, auth.credentials.did), 26 + ), 27 + ) 28 + .execute(); 29 + 30 + if (sandbox.length === 0) { 31 + throw new XRPCError(404, "Sandbox not found"); 32 + } 33 + 15 34 await tx 16 35 .delete(tailscaleAuthKeys) 17 36 .where(eq(tailscaleAuthKeys.sandboxId, input.id))