Skip to content

feat(demos): add an opt-in sandboxed shell to page recordings - #534

Merged
harlan-zw merged 2 commits into
mainfrom
feat/recorder-shell
Oct 7, 2026
Merged

harlan-zw merged 2 commits into
mainfrom
feat/recorder-shell

Conversation

@harlan-zw

Copy link
Copy Markdown
Collaborator

🔗 Linked issue

No linked issue.

❓ Type of change

  • 📖 Documentation
  • 🐞 Bug fix
  • 👌 Enhancement
  • ✨ New feature
  • 🧹 Chore
  • ⚠️ Breaking change

📚 Description

Some page Skills check their own output with a command. Page recordings had no shell, so the Agent skipped that check. The tt-a1i/archify/archify demo recorded on 2026-10-07 shipped broken this way.

pnpm demo:record ... --shell gives a page recording the sandboxed Bash that a video recording already gets. The default stays no shell. The parsed input carries a tagged Shell union: a video always gets sandboxed, and a page gets it only with the flag.

The second commit closes two leaks that --shell would have widened to pages:

  • The sandboxed shell inherited the recorder's whole environment, API keys included. The Agent now starts with an allowlisted environment.
  • The SSH agent socket in $XDG_RUNTIME_DIR was reachable. The sandbox now denies that folder, which also holds the GPG agent, keyring and session bus sockets.

Both changes apply to video recordings too.

--shell alone does not let archify run its finalize check. That check needs bin/archify.mjs, and skilld run does not print executable files.

The demo manifest does not record whether a run had a shell. A visitor's own Agent usually has a shell, so a shell run is the normal case. Video demos already run with one and record nothing, and nothing on the site would read the field.

📝 Migration

A verification recording uploaded four media files that nothing references. With CLOUDFLARE_ACCOUNT_ID set to the account in wrangler.jsonc, delete them:

for key in desktop-b44cdc640b.jpg desktop-poster-3f9594f3a3.jpg mobile-2423851416.jpg mobile-poster-193c740112.jpg; do
  cf r2 objects delete "demos/tt-a1i/archify/archify/$key" --bucket-name skilld-demo-media
done

Keep desktop-35a0f40375.jpg and desktop-poster-9575d21ad9.jpg under the same prefix. The live archify demo uses them.

🤖 AI disclosure: Harlan Agent Kit modified this description. My AI open-source policy.

Some page Skills gate their own output with a CLI step. Without a shell,
the Agent skips that step and a broken page can ship, as the archify
diagram did. `--shell` gives a page recording the sandboxed Bash a video
recording already gets. The default stays no shell.
A probe showed the sandboxed shell inherited the recorder's environment,
including API keys, and could reach the SSH agent, GPG agent, keyring
and session bus sockets under the runtime folder. Any Skill text that
gets a shell could use them. The Agent now gets an allowlisted
environment, and the sandbox denies the socket folders.
@harlan-zw harlan-zw added the harlan-agent-review Pull request triage requires an adversarial Review. Adding this label manually always forces one. label Oct 7, 2026
@harlan-zw

Copy link
Copy Markdown
Collaborator Author

Agent note (Claude Code, Harlan Agent Kit pr Skill). Checked by hand, since CI does not run the recorder:

  • Recorded tt-a1i/archify/archify with --shell and the sequence diagram prompt, before the env fix. Inside the sandbox the Agent ran node and a headless Chrome, screenshotted its page, and fixed label overflow from the screenshot.
  • Archify finalize did not run. skilld run printed "skilld will not print this executable file. Install the Skill to use it." for bin/archify.mjs, and the Agent followed the Skill's no-shell fallback instead.
  • The desktop and phone screenshots showed normal stroke widths and filled panels. The manifest and server/demos changes from that run are not in this PR.
  • A probe ran the committed record() with the sandboxed shell. ls -la ~/.ssh showed an empty folder. cat of a marker file in ~/.config failed with "No such file or directory", and the Read tool refused it as denied.
  • In the same probe, touch ~/x failed with "Read-only file system". curl https://example.com was blocked by the sandbox, and https://registry.npmjs.org/skilld answered 200.
  • Before the second commit, the sandboxed shell saw OPENAI_API_KEY, GEMINI_API_KEY and the SSH agent socket. After it, /run/user/1000 was empty in the sandbox, ssh-add -l failed with "Operation not permitted", and no API key was set. npx and headless Chrome still worked.
  • Claude Code still sets its own CLAUDE_CODE_* variables in the shell, including a messaging token. This change cannot remove them.
  • No video recording ran after the env allowlist.

@harlan-github-agent harlan-github-agent Bot added harlan-agent-review-required Pull request triage requires an adversarial Review for this head commit. and removed harlan-agent-review Pull request triage requires an adversarial Review. Adding this label manually always forces one. labels Oct 7, 2026
@harlan-zw
harlan-zw merged commit 8a291ce into main Oct 7, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

harlan-agent-review-required Pull request triage requires an adversarial Review for this head commit.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant