Connect a sandboxed AI agent to Anchor Browser over CDP
Run your agent in a Tenki Sandbox and drive an Anchor-managed browser with Playwright, with the API key kept out of images, templates and snapshots.
Guzman Pintos
8 min read
Your agent runs fine in a sandbox until it has to use a real website. You install Chromium, launch it headless, point Playwright at a login page, and get a challenge page instead of a form. Or the agent logs in once, and the next session starts from a clean profile and has to log in again, MFA code and all.
A Tenki Sandbox gives the agent an isolated Linux VM to run in. It does nothing to make a headless browser on a cloud IP look like a person at a keyboard. Anchor Browser takes on that part: it runs the browser on its side and hands your code a Chrome DevTools Protocol (CDP) URL to connect to. The rest of this post is about when that pairing is worth it, how to get a working session quickly, and where the Anchor API key should and shouldn't live.
Why your own headless Chrome gets stopped
Sites behind bot-detection services such as Cloudflare, Akamai, DataDome or reCAPTCHA are built to keep automated browsers out, and stock headless Chromium on a cloud IP does nothing to hide that it is one. Logins are the second wall. MFA and session handling assume a human, and a browser profile saved on one VM doesn't follow the agent to the next one unless you store the cookies, or the password, somewhere the next VM can read.
You can build the workaround yourself: a proxy pool, fingerprint patches, a captcha-solving service, a store for browser profiles, and upkeep every time a detection vendor changes something. Anchor sells that layer as a managed browser. Whether that's worth a second vendor depends on how much of your agent's work happens on sites that fight back.
How the work splits
Tenki's public tenki/anchor:stable image is the standard base image plus playwright-core and an anchor-smoke command. It has no local browser, no Anchor API key and no browser profile. Your agent code and the Playwright client run in the sandbox; the browser runs at Anchor, and the two talk over the CDP connection.
Because of that split, every page action is a network round trip. Playwright commands travel from the VM to Anchor and back, so chatty scripts that poll the DOM in tight loops pay for it. The browser also can't see the sandbox: a dev server on localhost inside the VM is not reachable from a browser running somewhere else.
The upside is that site logins can stay out of your VM. With Anchor's profiles or Identities, the site's cookies or credentials live on Anchor's side, and the only Anchor secret in the sandbox is the API key.
What Anchor adds, and what you have to turn on
Anchor lists a set of capabilities for agent browsing. They are Anchor's features and Anchor's claims; check Anchor's documentation for the current details. None of them is switched on in the default session the Tenki example opens. Each one takes a session option, a plan feature, or a request to Anchor.
| Capability | What Anchor says it does | How you get it |
|---|---|---|
| Anchor Chromium | A Chromium fork that Anchor describes as "fast, stealthy and secure" and "recognized as a human" | Anchor's browser; the options below are separate |
| Extra Stealth Mode | Anti-detection and human-like behavior | browser.extra_stealth; Anchor lists it as a Growth plan feature, and it only works with a proxy active |
| Proxy | Routes the session through Anchor's proxies, with country targeting | session.proxy, off by default |
| Captcha solving | Solves captchas during the session | browser.captcha_solver, off by default; needs an active proxy or a profile |
| Cloudflare Web Bot Auth | Signs requests so Cloudflare can identify the session as Anchor Browser | browser.web_bot_auth, off by default |
| Anchor VPN | Sends browser traffic through Anchor's own network with a dedicated IP | A premium feature the Anchor team enables on request |
| Profiles and Identities | A profile saves cookies and storage after one manual login; an Identity stores credentials and MFA, and Anchor logs in for you | browser.profile or identities when you create the session |
| Web Action Cache | In Anchor Tasks, replays steps from earlier runs as deterministic code instead of reasoning through every click again | Part of Anchor Tasks, not a session option |
If a site blocks you, the table is the place to start. If the problem is logging in, start with profiles and Identities, since they also keep site credentials out of your VM.
The short path
Create an Anchor API key, export it in your local shell, and pass it to the session and nowhere else:
export ANCHOR_API_KEY=<your-anchor-api-key>
tenki sandbox create \
--image tenki/anchor:stable \
--env "ANCHOR_API_KEY=$ANCHOR_API_KEY" \
--name anchor-demo
Then check the whole path before you write any code:
tenki sandbox exec --session anchor-demo -- anchor-smoke https://example.com
anchor-smoke creates an Anchor session, connects with Playwright, loads the URL, prints the final URL and page title as JSON, and ends the Anchor session. If that works, the key, the network path and the CDP connection all work.
Your own code follows the same shape: POST to Anchor's sessions API, chromium.connectOverCDP() on the returned cdp_url, and a DELETE of the Anchor session in a finally block. The Anchor docs page has the full script. Because playwright-core is installed under /home/tenki/node_modules, a script anywhere below /home/tenki can import it without another install:
tenki sandbox write --session anchor-demo --path /home/tenki/agent.mjs --data-file ./agent.mjs
tenki sandbox exec --session anchor-demo -- node /home/tenki/agent.mjs
The Tenki example sends an empty body, which gets you a default session. The body is where the options from the table go. Two settings are worth adding early. Anchor's session API defaults to a 20-minute maximum duration and a 5-minute idle timeout, so a long agent task needs a higher max_duration. And a named profile lets the agent reuse a login:
body: JSON.stringify({
session: { timeout: { max_duration: 60 } },
browser: { profile: { name: "crm-login" } },
}),
Anchor's docs describe the profile flow: create the session once with persist: true, log in, end the session to save the profile, then reuse it by name. Timeouts are in minutes.
Terminate the Tenki session when you're done:
tenki sandbox terminate anchor-demo
Keep the key out of images, templates and snapshots
Anyone holding the Anchor API key can call Anchor's API as your account, including starting browser sessions. Each of Tenki's reusable artifacts would hand it to more people or keep it around longer than the session that needed it.
Start with images and templates. Anything baked into an image is available to every session launched from it, and images can be published or shared with other workspaces. Template env values are plaintext configuration that shows up in read APIs. Template build secrets are encrypted and never reach the runtime, so they are the right tool for a private Git checkout during a build, but not a way to give a running session a key.
Snapshots keep the key around as well. A snapshot captures a session's disk and memory, and a restored session comes back with its running processes and in-memory state. Snapshot a session that has the key, and every session restored from that snapshot starts with it too. If you want a prepared starting point, a template is the tool for that, and the key goes in at session create time.
That leaves the session itself. Tenki's sessions docs are blunt that session environment variables are plaintext configuration and not a secret-management channel, and they warn against passing long-lived credentials that way. Passing ANCHOR_API_KEY with --env is a trade-off you accept for one session at a time, so keep that session short: end it when the work ends (--max-duration caps its lifetime), and replace the key in Anchor if you think it leaked. The shell history holds $ANCHOR_API_KEY, not the value.
Pin an exact build
tenki/anchor:stable moves to each newly published build. That's fine for trying things out. For an agent you've tested and deployed, the playwright-core version under it shouldn't change without you noticing. Resolve the tag once and launch the snapshot it points to:
tenki sandbox registry resolve tenki/anchor:stable
# image_id : <image-id>
# snapshot_id : <snapshot-id>
tenki sandbox create --image tenki/anchor@<snapshot-id>
A snapshot reference never moves, so it keeps launching that build after stable advances. Move to a newer build on purpose, after anchor-smoke and your own tests pass on it.
When you don't need Anchor
A remote, hardened browser solves a problem you may not have. If the site is yours, run your own Chrome inside the sandbox: your own app, staging environment or internal tool doesn't need to be fooled into letting you in. The same goes for testing, because a dev server running in the same VM is reachable on localhost from a local browser and not from a remote one. And many sites don't block automation at all. Plenty of documentation sites and public pages load fine in plain headless Chromium.
Running Chrome locally also keeps page content inside your VM and avoids a second bill. A browser for AI agents: run Chrome in a disposable sandbox walks through that setup, and the Chrome docs page covers headless and headed browsers, CDP and noVNC.
Next step
Start with the Anchor docs page: create a session from tenki/anchor:stable, run anchor-smoke, then swap in your own script. When a site starts blocking you or a login won't stick, turn on the matching option from Anchor's documentation.


