Other facts

Box URLs, takeover tokens, usage events, the one account that holds everything, a box's public IP address, and how boxes are kept apart.

  • Box URLs: sceneUrl, https://<id>-<key>.box.parallelsandbox.com, reaches the box's first declared service and is that service's url when it is web; each later service marked web has https://<id>-<key>-<targetPort>.box.parallelsandbox.com. Both go through the edge over HTTPS, and loading a page of a frozen box wakes it (Box states). When nothing listens on the service's targetPort in the box, the URL answers 503 with Retry-After and nothing answered on port N in this box; a GET (or HEAD) first waits up to 3 seconds for the service, so one restarting (a dev server re-optimizing its dependencies, a watcher reloading) usually answers instead. A URL with a wrong key gets the same 404 as a box that does not exist, and a -<port> with no web service behind it the same 404 as a port with no service.
  • Takeover token: an HMAC containing the box id and an expiry. The box only accepts one-time tokens issued by the control plane. The takeover's own expiry decides: 30 minutes from the request, extended to 30 minutes after the person first opens the link, so the link stays valid past the time in its token until then. GET /v1/takeovers gives each open takeover's token and appLink (parallelsandbox://box/<id>?t=<token>) next to takeoverUrl, which carries the account's language as &lang= when one is chosen.
  • Usage events, one per minute per running box and one per metered action. GET /v1/usage/events returns id, tenant, box, kind, quantity, unit, credits, at. Kinds: box minutes (box_runtime_*, charged per size unit), leased device minutes (device_runtime_*), outbound bytes (box_egress, everything the box sends, environment connections and links included), log bytes written (log_write) and stored (log_storage), stored files (artifact_storage).
  • One account holds everything: boxes, environments, links, published versions, the registry and the credits. Every connection of the account, OAuth or API key, acts on all of it; each teammate or agent signs in once, and API keys, for tools without OAuth, are made and revoked over REST (Team setup). An account has no members or roles: up to one GitHub, one Google and one Apple sign-in can be linked to it, and each opens the same account.
  • A box's traffic to the internet leaves from the public IP address of the host it runs on (curl -s https://checkip.amazonaws.com in the box shows it). AWS assigns it when the host starts, so boxes on different hosts leave from different addresses, and the addresses change as hosts come and go: they cannot be put on an allowlist. For an outside service that accepts only known IP addresses, list its host on a connection of the box's environment, for example api.partner.example:443: the box then reaches it through the connector, and the traffic leaves from your own network (Environments). Sites that block data-center addresses treat a box as a bot: YouTube playback in a box fails with Sign in to confirm you're not a bot. Test such a part by handing the page over with sandbox_review and open: "web", so it plays in the person's own browser, from their network.
  • Boxes stay isolated from each other except through the links you declare, which only connect boxes of the same account. From outside, a box is reachable only through its URLs (sceneUrl for its first service and the URLs of services marked web, over HTTPS, protected by the key in the URL; when the account has an IP allowlist, the source IP must also be on it, see the next item) and its takeover page. A box holds no cloud credentials of its own; the one exception is an environment's AWS role, whose temporary credentials only boxes started with that environment receive.
  • An IP allowlist for box URLs: PUT /v1/box-access with { "allowedIps": ["203.0.113.4", "198.51.100.0/24"] } (single addresses or CIDRs, at most 50, stored as normalized CIDRs); GET /v1/box-access returns allowedIps and updatedAt; [] turns it off. Once set, every box URL of the account (sceneUrl and each web service's URL) accepts only source IPs on the list, about 3 seconds after the change. Anything else gets 403, is not passed into the box and does not wake a frozen box: a browser opening a page sees "This IP address is not allowed", other requests get JSON {"status":"ip_not_allowed","ip":"<their IP>","error":"…"}; a wrong key still gets 404. Only IPv4 is matched for now (IPv6 entries are stored but match nothing). A box calling its own URL from inside is blocked too, because the request comes from its host's public IP: inside the box, use localhost or the service name. A person tapping Use it in the app or opening a review's web page goes through this check as well, so their IP must be on the list; the takeover page and the app's live screen and API are not affected. With a list set, sandbox_start and sandbox_status carry boxUrlAccess (allowedIps and a note). A malformed list gets 400 naming the entry.

Every tool and topic is listed in the tool reference.