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.

Make JWT generation async and fix sandbox auth

Return a Promise from generateJwt and use await in sandbox API
calls; change the JWT subject claim to "sub". Use c.var.did when
creating sandboxes server-side. Update current profile query to set
the profile atom. Adjust sandbox UI owner/permission checks and remove
a debug consola.info log.

+22 -15
+1
apps/api/src/lib/authVerfifier.ts
··· 2 2 import type express from "express"; 3 3 import jwt from "jsonwebtoken"; 4 4 import { env } from "./env"; 5 + import consola from "consola"; 5 6 6 7 type ReqCtx = { 7 8 req: express.Request;
+2 -2
apps/api/src/lib/generateJwt.ts
··· 1 1 import jwt from "@tsndr/cloudflare-worker-jwt"; 2 2 import { env } from "./env"; 3 3 4 - export default function generateJwt(did: string) { 4 + export default function generateJwt(did: string): Promise<string> { 5 5 return jwt.sign( 6 6 { 7 - did, 7 + sub: did, 8 8 }, 9 9 env.JWT_SECRET, 10 10 );
-1
apps/api/src/xrpc/io/pocketenv/actor/getProfile.ts
··· 135 135 }: WithAgent): Effect.Effect<WithUser, Error> => { 136 136 return Effect.tryPromise({ 137 137 try: async () => { 138 - consola.info(">> did", did); 139 138 return ctx.db 140 139 .select() 141 140 .from(tables.users)
+1 -1
apps/api/src/xrpc/io/pocketenv/sandbox/createSandbox.ts
··· 14 14 { 15 15 ...(auth?.credentials && { 16 16 headers: { 17 - Authorization: `Bearer ${generateJwt(auth.credentials.did)}`, 17 + Authorization: `Bearer ${await generateJwt(auth.credentials.did)}`, 18 18 }, 19 19 }), 20 20 },
+1 -1
apps/api/src/xrpc/io/pocketenv/sandbox/deleteSandbox.ts
··· 9 9 await ctx.sandbox.delete(`/v1/sandboxes/${params.id}`, { 10 10 ...(auth?.credentials && { 11 11 headers: { 12 - Authorization: `Bearer ${generateJwt(auth.credentials.did)}`, 12 + Authorization: `Bearer ${await generateJwt(auth.credentials.did)}`, 13 13 }, 14 14 }), 15 15 });
+1 -1
apps/api/src/xrpc/io/pocketenv/sandbox/startSandbox.ts
··· 9 9 await ctx.sandbox.post(`/v1/sandboxes/${params.id}/start`, undefined, { 10 10 ...(auth?.credentials && { 11 11 headers: { 12 - Authorization: `Bearer ${generateJwt(auth.credentials.did)}`, 12 + Authorization: `Bearer ${await generateJwt(auth.credentials.did)}`, 13 13 }, 14 14 }), 15 15 });
+1 -1
apps/api/src/xrpc/io/pocketenv/sandbox/stopSandbox.ts
··· 9 9 await ctx.sandbox.post(`/v1/sandboxes/${params.id}/stop`, undefined, { 10 10 ...(auth?.credentials && { 11 11 headers: { 12 - Authorization: `Bearer ${generateJwt(auth.credentials.did)}`, 12 + Authorization: `Bearer ${await generateJwt(auth.credentials.did)}`, 13 13 }, 14 14 }), 15 15 });
+1 -2
apps/sandbox/src/index.ts
··· 98 98 } while (true); 99 99 100 100 const record = await c.var.db.transaction(async (tx) => { 101 - const did = c.get("did"); 102 101 const user = await tx 103 102 .select() 104 103 .from(users) 105 - .where(eq(users.did, did || "")) 104 + .where(eq(users.did, c.var.did || "")) 106 105 .execute() 107 106 .then((res) => res[0]); 108 107 let [record] = await tx
+11 -3
apps/web/src/hooks/useProfile.ts
··· 1 1 import { useQuery } from "@tanstack/react-query"; 2 2 import { getCurrentProfile } from "../api/profile"; 3 + import { useAtom } from "jotai"; 4 + import { profileAtom } from "../atoms/profile"; 3 5 4 - export const useCurrentProfileQuery = () => 5 - useQuery({ 6 + export const useCurrentProfileQuery = () => { 7 + const [, setProfile] = useAtom(profileAtom); 8 + return useQuery({ 6 9 queryKey: ["currentProfile"], 7 10 queryFn: () => getCurrentProfile(), 8 - select: (response) => response.data, 11 + select: (response) => { 12 + const data = response.data; 13 + setProfile(data); 14 + return data; 15 + }, 9 16 }); 17 + };
+3 -3
apps/web/src/pages/sandbox/Sandbox.tsx
··· 63 63 <Navbar withLogo title="" project={data?.sandbox?.name} /> 64 64 {data?.sandbox && !isLoading && ( 65 65 <> 66 - {location.pathname.startsWith("/sandbox") && ( 66 + {!data?.sandbox?.owner && ( 67 67 <div 68 68 className="alert alert-soft alert-warning flex items-center bg-warning/10 border-none" 69 69 role="alert" ··· 132 132 </div> 133 133 </div> 134 134 {data?.sandbox?.status === "RUNNING" && 135 - ((profile && data?.sandbox?.uri?.includes(profile.did)) || 135 + ((profile && data?.sandbox?.owner?.did === profile.did) || 136 136 !data?.sandbox?.owner) && ( 137 137 <button 138 138 onClick={() => ··· 154 154 </button> 155 155 )} 156 156 {data?.sandbox?.status !== "RUNNING" && 157 - ((profile && data?.sandbox?.uri?.includes(profile.did)) || 157 + ((profile && data?.sandbox?.owner?.did === profile.did) || 158 158 !data?.sandbox?.owner) && ( 159 159 <button 160 160 onClick={() =>