Daytona Integration
CodeJam uses Daytona to provide ephemeral, isolated sandbox environments where users can execute code safely during boss battles and coding challenges.
Overview
The Daytona integration enables:
Ephemeral Sandboxes : Temporary, isolated execution environments
Multi-Language Support : Python, TypeScript, and JavaScript
Secure Execution : Code runs in isolated containers without affecting the main platform
Session Management : Track and manage user sandbox sessions for boss battles
Supported Languages
The integration supports three programming languages:
const supportedLanguages = [ 'python' , 'typescript' , 'javascript' ];
The language parameter is case-insensitive and normalized to lowercase before validation.
Architecture
The Daytona integration is implemented in convex/daytona.ts and consists of two main actions:
1. Creating Sandboxes
Sandboxes are created on-demand when a user starts a boss battle or challenge:
export const create = action ({
args: { language: v . string () },
handler : async ( ctx , args ) => {
const userId = await getAuthUserId ( ctx );
if ( ! userId ) throw new Error ( "Unauthorized" );
const daytona = getDaytona ();
// Verify language is supported by SDK
const supportedLanguages = [ 'python' , 'typescript' , 'javascript' ];
const lang = args . language . toLowerCase ();
if ( ! supportedLanguages . includes ( lang )) {
throw new Error ( `Unsupported language: ${ args . language } ` );
}
const sandbox = await daytona . create ({ language: lang });
return { sandboxId: sandbox . id };
},
});
2. Executing Code
Once a sandbox is created, code can be executed within it:
export const run = action ({
args: { sandboxId: v . string (), code: v . string () },
handler : async ( ctx , args ) => {
const userId = await getAuthUserId ( ctx );
if ( ! userId ) throw new Error ( "Unauthorized" );
const daytona = getDaytona ();
let sandbox ;
try {
sandbox = await daytona . get ( args . sandboxId );
} catch ( e ) {
console . error ( "Failed to get sandbox:" , e );
throw new Error ( "Could not restore sandbox session. Please refresh." );
}
// Ensure sandbox is started and ready
try {
if ( typeof sandbox . start === 'function' ) {
await sandbox . start ();
}
if ( typeof sandbox . waitUntilStarted === 'function' ) {
await sandbox . waitUntilStarted ();
}
} catch ( err ) {
console . warn ( "Sandbox start/ready check failed:" , err );
}
const response = await sandbox . process . codeRun ( args . code );
return response . result ;
},
});
SDK Initialization
The Daytona SDK is initialized with an API key from environment variables:
const getDaytona = () => {
const apiKey = process . env . DAYTONA_API_KEY ;
if ( ! apiKey ) throw new Error ( "DAYTONA_API_KEY not configured" );
return new Daytona ({ apiKey });
};
Ensure DAYTONA_API_KEY is properly configured in your environment variables before using the integration.
Boss Session Tracking
CodeJam tracks active sandbox sessions for boss battles using the boss_sessions table. This ensures users can resume their sessions and prevents multiple concurrent sessions.
Schema
boss_sessions : defineTable ({
userId: v . id ( "users" ),
sandboxId: v . string (),
language: v . string (),
status: v . union ( v . literal ( "active" ), v . literal ( "closed" )),
createdAt: v . number (),
}). index ( "by_user_status" , [ "userId" , "status" ])
Creating Session Records
When a sandbox is created, a session record is also created:
export const createSessionRecord = mutation ({
args: {
sandboxId: v . string (),
language: v . string (),
},
handler : async ( ctx , args ) => {
const userId = await getAuthUserId ( ctx );
if ( ! userId ) throw new Error ( "Unauthorized" );
// Close existing active sessions for this user
const existing = await ctx . db
. query ( "boss_sessions" )
. withIndex ( "by_user_status" , q => q . eq ( "userId" , userId ). eq ( "status" , "active" ))
. collect ();
for ( const session of existing ) {
await ctx . db . patch ( session . _id , { status: "closed" });
}
return await ctx . db . insert ( "boss_sessions" , {
userId ,
sandboxId: args . sandboxId ,
language: args . language ,
status: "active" ,
createdAt: Date . now (),
});
},
});
Retrieving Active Sessions
To restore a user’s active sandbox session:
export const getActiveSession = query ({
args: {},
handler : async ( ctx ) => {
const userId = await getAuthUserId ( ctx );
if ( ! userId ) return null ;
return await ctx . db
. query ( "boss_sessions" )
. withIndex ( "by_user_status" , ( q ) =>
q . eq ( "userId" , userId ). eq ( "status" , "active" )
)
. first ();
},
});
Usage Flow
User starts boss battle
When a user initiates a boss battle, CodeJam determines the required language for the challenge.
Create sandbox
The create action is called with the language parameter, creating a new Daytona sandbox.
Record session
A boss_sessions record is created, linking the sandbox ID to the user and marking any previous sessions as closed.
Execute code
As the user writes and submits code, the run action executes it in the sandbox and returns results.
Session cleanup
When the battle ends or the user creates a new session, the previous session is marked as “closed”.
Error Handling
The integration includes robust error handling:
Language Validation
Sandbox Retrieval
Sandbox Startup
if ( ! supportedLanguages . includes ( lang )) {
throw new Error ( `Unsupported language: ${ args . language } ` );
}
Security Considerations
Authentication Required : All sandbox operations require authentication via getAuthUserId
Isolated Execution : Code runs in ephemeral containers, isolated from the main platform
Session Ownership : Users can only access their own sandbox sessions
API Key Security : The Daytona API key is stored securely in environment variables
Next Steps
Boss Battles Learn how boss battles use Daytona sandboxes
Campaign System Explore the campaign structure and progression