X3DStudios

Chrome extension

The X3D browser extension: capture a region of any page, turn it into a 3D model, order the print — how pairing works, what its token can do, and what it cannot.

The X3D browser extension captures a region of whatever page you are on, sends the image to the model generator, and lets you order the result printed and posted without leaving the tab. It is a Manifest V3 Chrome extension that talks to the same generation and print endpoints the website uses, authenticated with its own token.

Not publicly released
The extension is not in the Chrome Web Store and there is no download on x3dstudios.com. Manifest version is 0.1.0. Everything on the server side of it is built and live — the pairing routes, the token auth, the consent page at /connect/extension — so this page describes a real integration, not a plan. But there is no install button today. If you want access, ask at /contact.

What it does#

  • Capture a rectangular region of the current tab, by click or with Alt+Shift+X.
  • Read the page for context — schema.org product data, OpenGraph tags, or the h1 — to pre-fill the prompt.
  • Warn when a capture comes back blank, which is what a DRM-protected video looks like to a screenshot.
  • Send the image to the generator and poll for progress. Generation runs in the service worker, so you can close the popup and it keeps going.
  • Order the finished model printed and shipped, with a shipping form in the popup.
  • Show your credit balance and whether a card is on file, at the moment you open the popup rather than at the moment you submit.

Chrome 116 or newer. Permissions requested are activeTab, scripting and storage, with one host permission: https://x3dstudios.com. It has no permission to read any other site in the background.

How pairing works#

Pairing follows the device-authorization pattern used by TV sign-in flows. The extension invents a secret, you approve that secret while signed in on the website, and the extension exchanges it for a token. The alternative — the website messaging an extension directly by ID — was rejected deliberately: the extension ID would arrive in the URL, so anyone who could get you to open a link could have a token minted into an extension of their choosing.

  1. 1
    The extension generates a nonce

    32 random bytes as 64 hex characters. Both pairing routes reject anything that is not exactly that shape with 400 and the message Malformed pairing code.

  2. 2
    It opens the consent page

    /connect/extension?code=<nonce>&label=<name>. The page lists what is being granted in plain terms: spending generation credits, placing print orders that charge your card, and reading your credit balance. If you are signed out it sends you to sign in and comes back.

  3. 3
    You approve

    The page posts to /api/extension/pair/approve, which is session-authenticated only. A bearer token cannot reach it — otherwise an extension that already had access could mint itself more. Only the hash of the nonce is stored, so the row is worth nothing if it leaks.

  4. 4
    The extension claims the token

    It polls /api/extension/pair/claim with the nonce it kept. Before approval that returns 202 with { pending: true } — polling while the consent tab is open is the normal case, not an error. After approval it returns the token once, and the pairing row is deleted in the same transaction, so a replayed claim finds nothing.

  5. 5
    The token is stored in the extension

    It looks like x3d_ext_ followed by 48 hex characters. The server keeps only a SHA-256 hash plus a short non-secret display prefix.

An approved pairing expires after ten minutes
If the extension does not claim within ten minutes the pairing is deleted and the link stops working. Open the extension and choose Connect account again to start a fresh one.

The pairing and session endpoints#

POST/api/extension/pair/approveSigned in

The consent page's submit. Body: { code, label }. Signed-in browser session only.

POST/api/extension/pair/claimNo auth

Exchange the nonce for a token. Unauthenticated by design — the nonce is the credential. 202 while pending.

GET/api/extension/meExtension token

Bearer x3d_ext_… — returns email, name, credits and hasPaymentMethod. 401 { error: not_connected } for a revoked token.

POST/api/extension/revokeSigned in

Disconnect. Body: { id } or { all: true }. Session only — a token must not be able to manage tokens.

POST /api/extension/pair/approve and /claim
codestringrequired
The pairing nonce: exactly 64 hex characters. Anything else is 400 Malformed pairing code.
labelstringoptionaldefault Chrome extension
A name for the connection, truncated to 60 characters. Shown on the consent page so you know what you are approving.

What the token can do#

EndpointAccepts an x3d_ext_ token?Notes
POST /api/generateYesChecked before the session cookie. Rate limit, credits and history all key off your email, so it is billed exactly like a request from the website.
POST /api/generate/startYesAsync generation. Returns { jobId } straight away and runs in the background.
GET /api/generate/status?jobId=…YesPoll for progress. 403 if the job belongs to someone else.
POST /api/print/ordersYesPlace a print order, including by modelUrl for a model X3D already stores.
GET /api/print/orders/{code}NoStatus reads accept only an x3d_live_ API key. See the limitation below.

Ordering by modelUrl is why the extension does not have to download and re-upload a model it just generated. A generated model already sits in X3D storage behind a gs:// URI the browser cannot fetch; the order route accepts that URI directly and re-checks that the model belongs to your account before using it.

An extension token can place an order but cannot read it back
POST /api/print/orders accepts extension tokens. GET /api/print/orders/{code} does not — it resolves x3d_live_ keys only, and an extension token gets 401 Invalid or missing API key. The extension therefore shows you the order code and the statusUrl and hands you off to the web page for tracking.

Limits worth knowing#

  • Generation spends credits, at the same rate as the website. Every extension generation starts from an image, which adds one credit to the base: a fast draft from a capture costs 2 credits and a hi-fi one costs 3, before any mesh-quality or texture-resolution options.
  • Generation is rate limited at 20 starts per hour per account, shared with the website.
  • Placing an order charges the card on your account. The consent page says so, and every order still needs two presses in the popup — Print & ship it, then Place order. An estimate is shown with the finished model; the amount actually charged is worked out from the file when the order is submitted, which is what the order screen tells you.
  • There is no extension manager in /profile yet. The consent page points there, but the page has no extension section — revoking today means calling POST /api/extension/revoke from a signed-in browser.
  • Revoking marks the token revoked rather than deleting it, so when a connection was disconnected stays answerable.
  • The extension token is separate from your print-API key on purpose. An extension runs inside every page you visit, which is a far more likely place for a credential to leak, so it is minted per connection and revocable on its own without breaking a server integration.