Skip to content

Instantly share code, notes, and snippets.

@roninjin10
Created March 13, 2026 22:26
Show Gist options
  • Select an option

  • Save roninjin10/fb9282e57e35893ba9d60e7f11ddccaa to your computer and use it in GitHub Desktop.

Select an option

Save roninjin10/fb9282e57e35893ba9d60e7f11ddccaa to your computer and use it in GitHub Desktop.
JJHub vm and sandbox product doc

JJHub Workspaces and Sandbox Specification

This document defines the product model for JJHub cloud workspaces, preview environments, and sandboxed execution. It covers the end-user experience across CLI, API, and web UI. Implementation details of the underlying VM platform are outside the scope of this spec.

1. Overview

JJHub workspaces are cloud development environments attached to repositories. Every bookmark in a repository can have an associated workspace. Workspaces are created lazily on first access and hibernate automatically when idle. They preserve full memory state across sessions so developers resume exactly where they left off.

Workspaces serve three product purposes:

  • interactive cloud development with terminal access
  • preview environments for Landing Requests
  • sandboxed execution for workflows, CI, and AI agents

All three use the same underlying workspace primitive. There is no separate "runner" or "sandbox" concept visible to users.

2. Core Concepts

2.1 Workspace

A workspace is a persistent cloud environment scoped to a repository bookmark and a user. Each workspace contains:

  • a full clone of the repository at the bookmark's head
  • a Linux environment with the tools defined by the repository's workspace template
  • any running services (dev servers, databases, etc.)
  • the user's session state (open files, terminal history, running processes)

Workspaces have five states:

  • starting — environment is being provisioned
  • running — actively executing, consuming compute
  • suspended — hibernated with full memory state preserved, storage cost only
  • stopped — shut down, disk preserved but memory state lost
  • failed — provisioning or execution error

2.2 Workspace Template

A workspace template defines the base environment for a repository's workspaces. Templates are defined in .jjhub/workspace.ts and committed to the repository.

import { defineWorkspace } from "@jjhub-ai/workflow";

export default defineWorkspace({
  // Tools and runtimes to install (cached in base layer)
  tools: {
    bun: "latest",
    jj: "latest",
  },

  // System packages
  packages: ["curl", "git", "jq", "build-essential"],

  // Install command (runs once after clone, cached in snapshot)
  install: "bun install",

  // Services to run in the workspace
  services: {
    "dev-server": {
      command: "bun run dev",
      port: 3000,
    },
    database: {
      command: "postgres -D /var/lib/postgresql/data",
      port: 5432,
    },
  },

  // Environment variables (non-secret, committed to repo)
  env: {
    NODE_ENV: "development",
    DATABASE_URL: "postgresql://localhost:5432/dev",
  },

  // Linux user configuration
  user: "developer",
});

Templates are cached as layered snapshots. The base layer (tools, packages, install) is built once and reused across all workspaces for the repository. The outer layer (repo checkout at specific revision, user config, secrets) is applied per workspace.

When no .jjhub/workspace.ts exists, JJHub uses a default template with Bun, jj, and git.

2.3 Preview Environment

A preview environment is a workspace that is automatically created for every Landing Request. It runs the repository's preview configuration and exposes the result on a public URL.

Preview configuration is defined in .jjhub/preview.ts:

import { definePreview } from "@jjhub-ai/workflow";

export default definePreview({
  // Port to expose as the preview URL
  port: 3000,

  // Install and start commands
  install: "bun install",
  start: "bun run dev",

  // Environment variables for preview
  env: {
    NODE_ENV: "preview",
  },

  // Optional: full control via setup function
  setup: async (workspace) => {
    await workspace.exec("bun install");
    await workspace.exec("bun run db:seed");
  },

  // Optional: additional services
  services: {
    database: {
      command: "postgres -D /var/lib/postgresql/data",
      port: 5432,
    },
  },
});

When no .jjhub/preview.ts exists but .jjhub/workspace.ts defines a service with a port, that service is used as the preview.

Preview environments:

  • are created automatically when a Landing Request is opened
  • are suspended when the Landing Request is idle
  • resume automatically on access (visiting the preview URL)
  • are deleted when the Landing Request is landed or closed
  • use ephemeral persistence (no long-term storage cost)

Preview URLs follow the pattern {lr-number}-{repo}.preview.jjhub.tech.

2.4 Workspace Sharing

Workspaces support collaborative access with permission levels:

Permission Capabilities
owner Full control, manage sharing, delete
editor Terminal access, file read/write, service management
viewer Read-only file access, view service logs, view terminal replay

Sharing options:

  • share with specific users (by username)
  • share with all repository collaborators
  • share with anyone who has the link (viewer only)

Each collaborator gets a separate Linux user and SSH identity scoped to their permission level. Multiple users can access the same workspace concurrently.

2.5 Workspace Snapshots

A snapshot captures the complete state of a workspace (memory, disk, running processes) as an immutable image. Snapshots can be:

  • created manually from a running workspace
  • used to create new workspaces instantly
  • shared with teammates as reusable environment templates

3. CLI Surface

3.1 Workspace Lifecycle

jjhub workspace create [--repo OWNER/REPO]
    --bookmark <name>          Bookmark to attach to (default: current)
    --snapshot <id>            Create from snapshot
    --template <name>          Use named template
    --persistence <type>       ephemeral | sticky | persistent (default: sticky)
    --idle-timeout <seconds>   Auto-suspend timeout (default: 1800)
    --no-idle-timeout          Never auto-suspend

jjhub workspace list [--repo OWNER/REPO]
    --state <filter>           running | suspended | stopped | all (default: all)
    --bookmark <name>          Filter by bookmark

jjhub workspace view <id> [--repo OWNER/REPO]
    Shows: status, bookmark, uptime, services, preview URL, sharing, persistence

jjhub workspace delete <id> [--repo OWNER/REPO]

jjhub workspace suspend <id> [--repo OWNER/REPO]
    Hibernate with full state preservation. Resume is instant.

jjhub workspace resume <id> [--repo OWNER/REPO]
    Resume from suspended state. Sub-second.

3.2 Terminal Access

jjhub workspace ssh [id] [--repo OWNER/REPO]
    --bookmark <name>          SSH into workspace for this bookmark (default: current)
    --user <username>          SSH as specific Linux user
    --print-command            Print SSH command instead of connecting

    If no workspace exists for the bookmark, one is created automatically.
    If the workspace is suspended, it resumes automatically on connect.

3.3 Remote Execution

jjhub workspace exec <id> <command> [--repo OWNER/REPO]
    --user <username>          Run as specific user
    --timeout <seconds>        Execution timeout (default: 120)

jjhub workspace run <id> [--repo OWNER/REPO]
    --code <typescript>        TypeScript code to execute
    --file <path>              Local .ts file to execute in workspace
    --workdir <dir>            Working directory inside workspace

3.4 File Operations

jjhub workspace files <id> ls <path> [--repo OWNER/REPO]
jjhub workspace files <id> cat <path> [--repo OWNER/REPO]
jjhub workspace files <id> write <path> [--repo OWNER/REPO] [--stdin]
jjhub workspace files <id> watch [--repo OWNER/REPO]
    Stream file change notifications in real-time.

3.5 Service Management

jjhub workspace services <id> [--repo OWNER/REPO]
    List all services with status (running, stopped, failed).

jjhub workspace logs <id> [--repo OWNER/REPO]
    --service <name>           Specific service (default: all)
    --follow                   Stream logs in real-time
    --lines <n>                Number of historical lines (default: 100)

jjhub workspace restart <id> <service> [--repo OWNER/REPO]

3.6 Forking and Snapshots

jjhub workspace fork <id> [--repo OWNER/REPO]
    --name <name>              Name for the forked workspace
    Copy-on-write clone of a running workspace. Original is not paused.

jjhub workspace snapshot create <id> [--repo OWNER/REPO]
    --name <name>              Name for the snapshot

jjhub workspace snapshot list [--repo OWNER/REPO]

jjhub workspace snapshot delete <id> [--repo OWNER/REPO]

3.7 Sharing

jjhub workspace share <id> [--repo OWNER/REPO]
    --user <username>          Share with specific user
    --team <team>              Share with team
    --collaborators            Share with all repo collaborators
    --permission <level>       owner | editor | viewer (default: editor)

jjhub workspace unshare <id> [--repo OWNER/REPO]
    --user <username>          Revoke specific user
    --team <team>              Revoke team
    --all                      Revoke all sharing

3.8 Preview Environments

jjhub workspace preview <lr-number> [--repo OWNER/REPO]
    Show the preview URL for a Landing Request.
    If the preview is suspended, it resumes automatically.

jjhub workspace preview logs <lr-number> [--repo OWNER/REPO]
    --follow                   Stream preview service logs

Preview URLs are also displayed in:

  • jjhub lr view <number> output
  • jjhub lr list output (when a preview exists)

3.9 Workspace Status Streaming

jjhub workspace watch <id> [--repo OWNER/REPO]
    Stream workspace state changes in real-time.
    Shows: provisioning progress, service startup, state transitions.

4. API Surface

All workspace operations go through the JJHub API. Clients never interact with the underlying VM platform directly.

4.1 Workspace Lifecycle

POST   /api/repos/{owner}/{repo}/workspaces
GET    /api/repos/{owner}/{repo}/workspaces
GET    /api/repos/{owner}/{repo}/workspaces/{id}
DELETE /api/repos/{owner}/{repo}/workspaces/{id}
POST   /api/repos/{owner}/{repo}/workspaces/{id}/suspend
POST   /api/repos/{owner}/{repo}/workspaces/{id}/resume

4.2 Terminal and Execution

GET    /api/repos/{owner}/{repo}/workspaces/{id}/ssh
POST   /api/repos/{owner}/{repo}/workspaces/{id}/exec
POST   /api/repos/{owner}/{repo}/workspaces/{id}/run
GET    /api/repos/{owner}/{repo}/workspaces/{id}/terminal    (WebSocket)

4.3 File Operations

GET    /api/repos/{owner}/{repo}/workspaces/{id}/files/{path}
PUT    /api/repos/{owner}/{repo}/workspaces/{id}/files/{path}
GET    /api/repos/{owner}/{repo}/workspaces/{id}/files        (list directory)
DELETE /api/repos/{owner}/{repo}/workspaces/{id}/files/{path}
GET    /api/repos/{owner}/{repo}/workspaces/{id}/files/watch  (SSE)

4.4 Services

GET    /api/repos/{owner}/{repo}/workspaces/{id}/services
GET    /api/repos/{owner}/{repo}/workspaces/{id}/services/{name}/logs
POST   /api/repos/{owner}/{repo}/workspaces/{id}/services/{name}/restart
GET    /api/repos/{owner}/{repo}/workspaces/{id}/services/{name}/status

4.5 Forking and Snapshots

POST   /api/repos/{owner}/{repo}/workspaces/{id}/fork
POST   /api/repos/{owner}/{repo}/workspaces/{id}/snapshot
GET    /api/repos/{owner}/{repo}/workspace-snapshots
GET    /api/repos/{owner}/{repo}/workspace-snapshots/{id}
DELETE /api/repos/{owner}/{repo}/workspace-snapshots/{id}

4.6 Sharing

GET    /api/repos/{owner}/{repo}/workspaces/{id}/shares
POST   /api/repos/{owner}/{repo}/workspaces/{id}/shares
DELETE /api/repos/{owner}/{repo}/workspaces/{id}/shares/{share-id}
PATCH  /api/repos/{owner}/{repo}/workspaces/{id}/shares/{share-id}

4.7 Preview Environments

GET    /api/repos/{owner}/{repo}/landings/{number}/preview
POST   /api/repos/{owner}/{repo}/landings/{number}/preview         (manual trigger)
DELETE /api/repos/{owner}/{repo}/landings/{number}/preview
GET    /api/repos/{owner}/{repo}/landings/{number}/preview/logs    (SSE)

4.8 Streaming

GET    /api/repos/{owner}/{repo}/workspaces/{id}/stream            (SSE)
    Workspace state changes, service status, provisioning progress.

GET    /api/repos/{owner}/{repo}/workspaces/{id}/services/{name}/logs?follow=true  (SSE)
    Real-time service log streaming.

5. Web UI Surface

5.1 Workspace List (Repository Page)

The repository page includes a "Workspaces" tab showing:

  • all workspaces for the repository, grouped by bookmark
  • status badge (running, suspended, stopped)
  • owner avatar
  • last activity time
  • quick actions: SSH, suspend, resume, delete

5.2 Workspace Detail Page

The workspace detail page shows:

  • status and uptime
  • bookmark and revision
  • services list with status and logs
  • terminal access (embedded xterm)
  • file browser
  • sharing controls
  • snapshot history
  • preview URL (if attached to a Landing Request)

5.3 Landing Request Preview

The Landing Request detail page includes:

  • a "Preview" tab with an embedded iframe showing the preview environment
  • a "Open Preview" button that opens the preview URL in a new tab
  • preview service logs
  • preview status badge (starting, running, suspended)
  • the preview resumes automatically when the tab is viewed

5.4 Workspace Terminal

The terminal view provides:

  • full xterm terminal connected via WebSocket to SSH
  • terminal resize support
  • connection status indicator
  • workspace state in the header (running, services status)

5.5 Workspace in Issue Flow

When viewing an issue that has an active workflow run:

  • the issue page shows the workspace where the agent is working
  • live streaming of agent output
  • link to the workspace terminal for manual intervention
  • link to the resulting Landing Request when the workflow completes

6. Workspace-per-Bookmark Model

6.1 Lazy Creation

Workspaces are not created eagerly for every bookmark. They are created on first access:

  • user runs jjhub workspace ssh --bookmark feature-x
  • user opens a workspace from the UI for a specific bookmark
  • a workflow or agent task requires a workspace for a bookmark
  • a Landing Request triggers a preview environment

6.2 One Workspace per User per Bookmark

Each user can have at most one active workspace per bookmark in a repository. "Active" means any state except stopped. This constraint is enforced by the database.

If a user already has a workspace for a bookmark, subsequent access reuses it:

  • if running, connect directly
  • if suspended, resume automatically then connect
  • if starting, wait for it to finish then connect

6.3 Bookmark Tracking

When the bookmark advances (new commits are pushed), the workspace does not automatically update. The user must explicitly pull changes within their workspace. This matches the mental model of a local checkout — your workspace is your working copy.

7. Workflow and Agent Integration

7.1 Workflows Use Workspaces

Workflow steps that need code execution run inside workspaces. The workflow runtime creates ephemeral workspaces from cached snapshots, executes steps, and tears them down.

Workflow-created workspaces:

  • use ephemeral persistence (auto-deleted after use)
  • are not visible in the user's workspace list
  • use the repository's workspace template for consistent environments
  • have secrets injected via environment variables (never written to disk)

7.2 Issue Pipeline

The automated issue pipeline (Research, Plan, Implement, Review) uses workspaces at the Implement step:

  1. Workflow creates an ephemeral workspace from the repository's cached snapshot
  2. Agent runs inside the workspace with the plan artifact as input
  3. Agent writes code, runs tests, commits with jj
  4. Workflow collects change IDs and creates a Landing Request
  5. Workspace is deleted

The user can observe this in real-time via the issue page, the workflow run page, or the CLI (jjhub workflow watch).

7.3 CI Pipeline

CI steps (lint, test, build, e2e) run in ephemeral workspaces:

  • created from the repository's cached snapshot (sub-second startup)
  • each step can run in parallel in separate workspaces
  • results reported as commit statuses on the Landing Request
  • workspaces deleted after completion
  • logs and artifacts preserved in the workflow run record

8. Security Model

8.1 Workspace Isolation

Each workspace is a fully isolated virtual machine. Workspaces for different users and repositories share no state, filesystem, or network namespace.

8.2 Secret Handling

  • Secrets are injected as environment variables at runtime, never written to the filesystem
  • Repository secrets (configured via jjhub secret set) are available in workflow workspaces
  • User credentials (Claude auth, API keys) are injected per-session and scoped to the workspace
  • SSH tokens are temporary, per-session, and scoped to a specific workspace and Linux user

8.3 Access Control

Workspace access requires:

  • authentication with JJHub
  • repository read permission (minimum) for viewing
  • repository write permission for creating, executing, and modifying
  • explicit sharing grant for accessing another user's workspace

8.4 Network Isolation

Workspaces have outbound internet access for package installation and API calls. Inbound access is limited to:

  • SSH via the JJHub SSH proxy
  • preview URL traffic via the JJHub preview proxy
  • no direct network access between workspaces

9. Billing and Limits

Workspace usage is metered against the user's or organization's subscription plan:

Resource Metering
Workspace compute hours Time in running state, per-minute billing
Workspace storage Time in suspended state, per-GB/hour
Preview environments Count against workspace compute hours
Concurrent workspaces Plan limit (e.g., 5 concurrent for Pro)
Snapshots Count and storage against plan limits

Suspended workspaces cost significantly less than running workspaces. Users are encouraged to let idle timeouts suspend workspaces rather than keeping them running.

10. Terminology

Term Definition
Workspace A cloud development environment attached to a repository bookmark
Preview A workspace automatically created for a Landing Request with an exposed URL
Snapshot An immutable capture of a workspace's complete state
Template A repository-committed configuration defining the workspace environment
Fork A copy-on-write clone of a running workspace
Suspend Hibernate a workspace with full memory state preservation
Resume Wake a suspended workspace, restoring exact previous state

11. Implementation Notes

The workspace platform is an internal concern. User-facing surfaces (CLI, API, UI, documentation, error messages) must not reference the underlying VM provider. The product vocabulary is "workspace," "preview," "snapshot," "suspend," and "resume" — not VM, sandbox, container, or provider-specific terms.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment