Skip to content

Add @e2b/id: prefixed IDs for E2B resources - #1627

Open
mishushakov wants to merge 3 commits into
mainfrom
prefixed-id-packages
Open

mishushakov wants to merge 3 commits into
mainfrom
prefixed-id-packages

Conversation

@mishushakov

@mishushakov mishushakov commented Jul 30, 2026 •

Copy link
Copy Markdown
Member

Stack: 1 of 2. This is the JavaScript half; #1628 adds the Python twin on top. Split for review — this PR is self-contained and its tests pass with packages/python-id absent.

@e2b/id gives every service and SDK one spelling for a resource ID: a three-character kind prefix, an underscore, and the 26-character TypeID encoding of a UUID — the format E2B's Go services emit and accept (packages/shared/pkg/id in e2b-dev/infra, built on github.com/sumup/typeid). No dependencies, and no runtime-specific API — a test asserts src reaches for nothing but platform globals, so it works in Workers, Deno, Bun and browsers with no compat flags.

The encoding is the UUID's 128 bits in base 32 with the lowercase Crockford alphabet (0-9a-z minus i, l, o, u), most significant digit first, left-padded to 26 digits. 26 digits hold 130 bits, so the first character carries two zero bits and is always 0–7; anything above is rejected as overflow rather than masked, exactly as Go rejects it. The digits are the bits in order, so IDs sort like their UUIDs: a UUIDv7's IDs are ordered by mint time, and a batch minted together shares a long prefix.

Part of SDK-303

Usage

import { createId, decodeId, encodeId, isId, parseId } from '@e2b/id'

createId('project')                                          // 'prj_01kyjhkfvse96rg4d2qzd9enft'
encodeId('volume', '019fa519-bfc5-784d-9386-a5d7a93a692a')   // 'vol_01kyjhkfy5f16s71n5tymkmt9a'
decodeId('volume', 'vol_01kyjhkfy5f16s71n5tymkmt9a')         // '019fa519-bfc5-784d-9386-a5d7a93a692a'
parseId('vol_01kyjhkfy5f16s71n5tymkmt9a')                    // { kind: 'volume', uuid: '019fa519-…' }
isId('project', 'vol_01kyjhkfy5f16s71n5tymkmt9a')            // false

Id<K> is a template literal type, so a mismatch is a compile error and isId narrows:

function open(volume: Id<'volume'>) {}
open(createId('project'))               // Argument of type 'prj_${string}' is not assignable
if (isId('volume', input)) open(input)  // narrowed to Id<'volume'>

Errors carry a reason ('kind' | 'prefix' | 'length' | 'alphabet' | 'overflow' | 'uuid') plus actualKind, so callers branch on a field rather than on message prose.

kind project workspace secret volume sandbox user group
prefix prj wrk sec vol sbx usr grp

project, workspace and secret are the kinds Go defines, with the same prefixes; the other four are ahead of it.

Notes for reviewers

  • This is Go's current format, verified against Go's own tests. infra moved from a rotated RFC 4648 encoding to TypeID on 2026-08-21 (infra 78ab8bb55); an earlier revision of this PR implemented the pre-move format. The golden vectors now include every vector from object_test.go, prefix and all — including the "legacy body" case Go pins (prj_6zhag5xcjyf4sadahkmb6xrfqf → df8aa05e-…), which exists precisely because the two alphabets overlap and a string in the old format can decode silently to a different UUID under the new rules.
  • The kind registry is closed. A copy pinned to 0.1.0 rejects a structurally valid ID for a kind added later. Known and accepted for now, to be handled before an eighth kind ships.
  • Raw UUIDs are not accepted. Go's apiid boundary types take a UUID or a public ID; that is an HTTP-input convenience, not part of the codec, and this package implements the codec.
  • Wired into the release pipeline. Public, versioned from 0.0.0 with an "Initial release" changeset so the next release mints 0.1.0, gated on the id_tests workflow in release.yml, and published by changeset publish over npm's OIDC trusted publishing like every other package here. The @e2b/id name already exists on npm as a 0.0.0 placeholder with the trusted publisher attached, so 0.1.0 is the first real version and the release needs no manual step.

Verification

129 tests, plus lint/typecheck/build clean. Beyond the suite: the built dist contains zero node: imports, and the interop suite drives python3 over the whole 1306-value corpus with an arithmetic encoder stated independently of this code (int.from_bytes and the alphabet, nothing else).

CORPUS_DIGEST is the cross-implementation contract that #1628 pins from the other side.

🤖 Generated with Claude Code


Devin Review

@linear-code

linear-code Bot commented Jul 30, 2026

Copy link
Copy Markdown

SDK-303

@cla-bot cla-bot Bot added the cla-signed label Jul 30, 2026
@changeset-bot

changeset-bot Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4d95431

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@e2b/id Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cursor

cursor Bot commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

PR Summary

Low Risk
New standalone codec package with extensive tests and no changes to existing SDK runtime paths.

Overview
Nothing incorrect stands out in the new package, tests, or CI/release wiring for @e2b/id.

Reviewed by Cursor Bugbot for commit 4d95431. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

Package Artifacts

Built from d06589f. Download artifacts from this workflow run.

JS SDK (e2b@2.63.1-prefixed-id-packages.0, with @e2b/dockerfile-utils@0.1.2-prefixed-id-packages.0):

npm install ./e2b-dockerfile-utils-0.1.2-prefixed-id-packages.0.tgz ./e2b-2.63.1-prefixed-id-packages.0.tgz

CLI (@e2b/cli@2.23.1-prefixed-id-packages.0):

npm install ./e2b-cli-2.23.1-prefixed-id-packages.0.tgz

Code Interpreter JS SDK (@e2b/code-interpreter@2.9.2-prefixed-id-packages.0):

npm install ./e2b-code-interpreter-2.9.2-prefixed-id-packages.0.tgz

Desktop JS SDK (@e2b/desktop@2.5.1-prefixed-id-packages.0):

npm install ./e2b-desktop-2.5.1-prefixed-id-packages.0.tgz

ID JS (@e2b/id@0.0.1-prefixed-id-packages.0):

npm install ./e2b-id-0.0.1-prefixed-id-packages.0.tgz

Python SDK (e2b==2.63.0+prefixed.id.packages, with e2b-dockerfile-utils==0.1.1+prefixed.id.packages):

pip install ./e2b_dockerfile_utils-0.1.1+prefixed.id.packages-py3-none-any.whl ./e2b-2.63.0+prefixed.id.packages-py3-none-any.whl

Code Interpreter Python SDK (e2b-code-interpreter==2.11.9+prefixed.id.packages):

pip install ./e2b_code_interpreter-2.11.9+prefixed.id.packages-py3-none-any.whl

Desktop Python SDK (e2b-desktop==2.7.2+prefixed.id.packages):

pip install ./e2b_desktop-2.7.2+prefixed.id.packages-py3-none-any.whl

cursor[bot]

This comment was marked as resolved.

@mishushakov
mishushakov force-pushed the prefixed-id-packages branch from ebc5708 to 27a952b Compare July 30, 2026 16:12
@mishushakov mishushakov changed the title Add @e2b/id and e2b_id: prefixed IDs for E2B resources Add @e2b/id: prefixed IDs for E2B resources Jul 30, 2026
A three-character kind prefix, an underscore, and the 26-character TypeID
encoding of a UUID:

    prj_7mfb0gpp6c8dsaasre0asc7n3s
    sbx_01kyjhkfzbf7samagavqtvkg6s

The encoding is the one E2B's Go services use (packages/shared/pkg/id in
e2b-dev/infra, built on github.com/sumup/typeid): the 128 bits in base 32
with the lowercase Crockford alphabet, most significant digit first, so
every ID starts with 0-7 and sorts the way its UUID does. The Go suite's
own vectors are pinned here, prefix and all, so an ID minted on either
side decodes on the other to the same UUID. A leading character above 7
is rejected as overflow rather than masked, exactly as Go rejects it.

Kinds are project, workspace and secret — the three Go defines, with the
same prefixes — plus volume, sandbox, user and group ahead of it.

UUIDs are read and minted the way github.com/google/uuid does it, so
the two sides agree there too: encodeId takes exactly the four spellings
Go's Parse takes (hyphenated, urn:uuid:-prefixed, braced, bare) and
checks no version bits, and createUuid is NewV7 — a millisecond
timestamp and a 12-bit sequence bumped so every UUID a process mints is
strictly greater than the last, then 62 random bits — so IDs sort in
call order even within a millisecond.

No dependencies, and no runtime-specific API: a test asserts src reaches
for nothing but platform globals, so the package works in Workers, Deno,
Bun and browsers with no compat flags. The interop suite drives python3
over the whole corpus with an arithmetic encoder stated independently of
this code.

Wired into the release pipeline: public, versioned from 0.0.0 with an
"Initial release" changeset so the next release mints 0.1.0, gated on
the id_tests workflow in release.yml, and published by `changeset
publish` over npm's OIDC trusted publishing like every other package
here. The @e2b/id name already exists on npm as a 0.0.0 placeholder
with the trusted publisher attached, so 0.1.0 is the first real
version and publishing needs no manual step.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@mishushakov
mishushakov marked this pull request as ready for review November 12, 2026 16:03
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-12T16:06:11.189740Z 27a952b Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

claude[bot]

This comment was marked as resolved.

… @e2b/id

- decodeId and parseId throw InvalidIdError('prefix') for a non-string id
  (null, a number, ...) instead of a TypeError, so callers that map
  InvalidIdError to a 400 don't turn a bad JSON payload into a 500.
- Kind lookups in prefixOf and isId use own properties only, so
  encodeId('toString', ...) throws InvalidIdError('kind') instead of
  minting "function toString() { [native code] }_...".
- decodeId no longer slices to indexOf's -1 when the id has no separator,
  which named the wrong kind for inputs like "prjx".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@e2b-harness e2b-harness Bot added the review-requested Posted in #code-review-requests on Slack label Nov 13, 2026
Adds tests/uuid.test.ts, which exercises uuid.ts on its own instead of
through encodeId/createId:

- uuidToBytes: exact bytes, every hex digit in both cases, agreement with
  an independent formatter over the shared corpus, and each of Go's four
  forms probed one character short, one long and with a bad digit.
- bytesToUuid: all 256 byte values, the corpus, Buffers and Uint8Arrays
  from another realm, and wrong lengths.
- createUuid, on a fresh module per test so the monotonic state never
  leaks between them: version and variant on every mint, the 48-bit
  timestamp at 0, 2^32 +/- 1 and 2^48 - 1, random bits confined to rand_b,
  the sequence resetting on a new millisecond, and carry on overflow.

The tests caught two input-validation bugs, both now InvalidIdError('uuid'):
uuidToBytes threw a TypeError for a Symbol (from interpolating it into the
message), and bytesToUuid threw a TypeError for null and formatted plain
arrays and Uint16Arrays with values above 255 as "undefined".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

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

Labels

cla-signed review-requested Posted in #code-review-requests on Slack

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant