Skip to content

feat: e2b template aliases, --e2b-template-aliases and GET /templates/aliases/{alias} - #56

Merged
CMGS merged 1 commit into
masterfrom
feat/e2b-template-aliases
Sep 27, 2026
Merged

CMGS merged 1 commit into
masterfrom
feat/e2b-template-aliases

Conversation

@CMGS

@CMGS CMGS commented Sep 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Drop-in phase A, PR A3: template aliases.

Why. The e2b SDKs create the template base when none is named, and pools here are keyed by image, so Sandbox.create() found no pool and got 503.

The table. --e2b-template-aliases names a file of alias pool-image lines.

  • Blank lines and # comments are ignored.
  • The file is read at startup.
  • Startup fails on a malformed line or an alias named twice.
  • The API key file reader e2bAPIKeys became e2bFileLines(path, what) and reads both files. It passes the path through filepath.Clean for gosec now that the path is a parameter.

Behavior.

  • Create: a templateID the table names claims from the aliased image's pool. Any other templateID is the pool image, as before.
  • Replies: every reply keeps templateID as the pool image and fills alias with the image's first alias in sort order. That covers create, get, list, connect, resume and fork. e2b pairs a template id with its name, the SDKs map alias to name, and a stored templateID still creates the same sandbox.
  • The list's template filter accepts the alias or the image, as the spec says ("template ID or alias").
  • GET /templates fills each image's aliases.
  • GET /templates/aliases/{alias} answers the spec's TemplateAliasResponse, {"templateID": "<pool image>", "public": true}, or 404.
    • It is behind the API key, like every route but /health.
    • The JS SDK's Template.exists and the Python SDK's Template.alias_exists read 200 as true and 404 as false.
  • docs/e2b-compat.md: the new flag row, the create and template rows, the alias route row, and the read-path note.

Closure and budget

Head to merge: 4a900075c80662c1fcb99c5e1d2fb1df7e645c01, on master 518de6d.

New exported identifiers and their consumers:

  • Options.TemplateAliases: NewServer, and cmd/sandbox-apiserver sets it.
  • TemplateAliasResponse: templateAlias.

New unexported code: the aliases and imageAliases maps on Server, aliasOf, poolImage, templateAlias, e2bFileLines, and the E2BAliasesFile option.

Comment lines per file (added / removed):

file + −
pkg/e2bcompat/server.go 2 1
pkg/e2bcompat/types.go 1 0
cmd/sandbox-apiserver/main.go 2 2
other files 0 0
  • In server.go, the package-doc route table absorbs the alias route in an existing line, and Options.TemplateAliases gets its godoc.
  • In types.go, TemplateAliasResponse gets its godoc.
  • In main.go, the reader's godoc is renamed with it.

Imports: pkg/e2bcompat imports only pkg/scale from cocoonstack, and stays on encoding/json v1.

Hot path

  • Create gains one map lookup. The A3-03 A/B on the testbed below shows no difference.
  • Reads gain one map lookup per item.
  • The template list copies each image's alias slice, so an image without aliases still serializes [].

Hardware

Setup:

  • The kube L3 path.
  • sandboxd main acfca8b with an e2b-rt:24.04 pool, warm 4.
  • Arms side by side: master 518de6d (main), and this head with --e2b-template-aliases naming base ghcr.io/cocoonstack/sandbox/e2b-rt:24.04.
  • JS ran from a workstation through an ssh tunnel; Python and curl ran on the node.
row this head master
JS 2.51.0 Sandbox.create() with no template, then getInfo, list and Template.exists lands on the e2b-rt pool. getInfo and list give {templateId: "ghcr.io/cocoonstack/sandbox/e2b-rt:24.04", name: "base"}, and exists gives base true, unknown false: RESULT PASS ServiceBusyError ... no warm sandbox available for template "base"
JS 2.50.0, same RESULT PASS the same 503
Python 2.51.0 Sandbox.create(), get_info, list, Template.alias_exists template_id is the image, name base, alias_exists true/false: RESULT PASS ServiceBusyException: 503 ... "base"
JS 2.51.0, JS 2.50.0 and Python 2.51.0 full matrix RESULT PASS ×3 —
A3-01, curl, GET /templates/aliases/base 200 {"templateID":"ghcr.io/cocoonstack/sandbox/e2b-rt:24.04","public":true}; unknown 404; no key 401 404, no route
A3-02, curl, create templateID: base {"templateID":"ghcr.io/cocoonstack/sandbox/e2b-rt:24.04","alias":"base"}. /templates gives [{"templateID": "<image>", "aliases": ["base"]}]. The list filter finds it by base and by the image. —
A3-03, create by image, 20 rounds per arm, interleaved, order swapped, each a warm hit p50 1.08 ms, p90 1.12 ms, 20/20 201 p50 1.04 ms, p90 1.23 ms, 20/20 201

Review and gates

/simplify:

  • Reuse: the key-file reader now serves both files.
    • Applied: getDetail delegates to the new getDetailWith instead of duplicating it.
    • Skipped: merging the key and alias parsers, since their arity and duplicate rules differ.
  • Simplification: both maps are earned: one answers create and the lookup, the other the reads.
  • Efficiency: only map lookups, plus one slice copy per template for the JSON shape.
  • Altitude:
    • Applied: reads keep templateID as the image with alias filled, on every reply, create included. The first draft reported the alias as templateID and was changed before this PR opened.
    • Applied: a doc line that the file is read at startup.
    • Skipped: naming the pool in the 503 message, since the message names the templateID the caller sent and the alias route exposes the mapping.
    • Skipped: filling the spec's names, which no SDK reads.
    • Accepted [contrived]: an alias that shadows another pool's image, since the operator writes the file.

/code, the changed files read in full: local variables named names shadowed the imported names package and are renamed aliasNames.

Mutations, each caught by a test:

  • the alias not mapped on create
  • reads missing the alias
  • the filter not normalized
  • the lookup always 404
  • the template list without aliases
  • a duplicate alias accepted
  • aliases unsorted

Gates:

  • make lint: 8/8 0 issues. on darwin and on linux.
  • make fmt-check: ok.
  • asl: 0 findings on both platforms.
  • go mod tidy -diff: clean.
  • go test -race: 11 ok.

…/aliases/{alias}

The SDKs create "base" when no template is named, and pools are keyed by
image, so Sandbox.create() found no pool. An alias file maps a templateID
to a pool image: create claims from the aliased pool; every reply keeps
templateID as the pool image and fills alias with its first alias, as
e2b pairs a template id with its name; the list filter accepts either
spelling; the template list carries each image's aliases; and
GET /templates/aliases/{alias} answers the SDK's alias check. The api key
file reader now serves both files.
@CMGS
CMGS merged commit 2ef6a7c into master Sep 27, 2026
2 checks passed
@CMGS
CMGS deleted the feat/e2b-template-aliases branch September 27, 2026 07:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant