PORTAL v2

SDK API

SDK endpoints are the stable lease protocol between a Portal tunnel process and a relay. Normal JSON endpoints use the shared envelope from API Reference. GET /sdk/connect is the only SDK endpoint that upgrades from ordinary HTTP into the reverse transport.

Flow

  1. GET /sdk/domain checks relay compatibility, optional ENS support, and optional relay-owned Sui x402 control-plane facilitator support.
  2. POST /sdk/register/challenge creates a SIWE challenge for the requested identity.
  3. The SDK signs the returned siwe_message.
  4. POST /sdk/register exchanges the signature for a lease access_token and a generic reverse_endpoint.
  5. The SDK keeps the lease alive with /sdk/renew and opens reverse streams at the returned endpoint using its opaque capability.
  6. POST /sdk/unregister removes the lease.

Endpoints

MethodPathAuthBodyData
GET/sdk/domainNonenoneDomainResponse
POST/sdk/register/challengeNoneRegisterChallengeRequestRegisterChallengeResponse
POST/sdk/registerSIWE signature bodyRegisterRequestRegisterResponse
POST/sdk/renewlease token bodyRenewRequestRenewResponse
POST/sdk/reverselease token bodyReverseEndpointRequestReverseEndpoint
POST/sdk/unregisterlease token bodyUnregisterRequest{}
GET/sdk/connectreverse capability header or WebSocket subprotocolnoneraw stream or WebSocket/yamux session
POST/sdk/cachelease token headerStaticCacheManifest JSONStaticCacheStatus
PUT/sdk/cachelease token headermanifest and file bytes as multipartStaticCacheStatus
DELETE/sdk/cachelease token headernoneStaticCacheStatus

Domain

GET /sdk/domain returns:

FieldTypeNotes
protocol_versionstringSDK tunnel protocol version
release_versionstringrelay software release
cacheStaticCacheLimitsoptional; advertised when static caching is enabled
ensENSStatusgasless ENS status
x402X402FacilitatorInforelay-owned Sui x402 control-plane facilitator status

ENSStatus:

FieldType
enabled, verifiedboolean
provider, address, dnssec_state, ds_record, message, last_errorstring

X402FacilitatorInfo:

FieldType
enabledboolean
url, network, network_name, supported_url, pay_tostring

This object describes the relay’s own optional x402 facilitator for control-plane resources. It is separate from tunnel-owned routed HTTP payments, which are configured locally by the tunnel process.

Register Challenge

RegisterChallengeRequest:

FieldTypeRequiredNotes
identityIdentityyesname and address
allow_canonical_fallbackbooleannoallow canonical-only registration when the friendly alias is unavailable; defaults to false for protocol-10 compatibility
metadataLeaseMetadatanopublic lease metadata
overlaybooleannoprefer an IVNP overlay path when available; defaults to false
ttlnumbernoRequested seconds; non-positive values use 120, maximum 600. Larger values return HTTP 400.
udp_enabledbooleannorequest UDP transport
tcp_enabledbooleannorequest dedicated TCP port
cachebooleannoExplicitly permit static storage and relay TLS termination; default false
cache_ttlnumbernoRequested offline seconds; 0 uses relay policy, positive values are clamped

Cache opt-in is incompatible with raw TCP/UDP leases.

RegisterChallengeResponse:

FieldType
challenge_idstring
expires_atstring
siwe_messagestring

Register

RegisterRequest:

FieldTypeRequired
challenge_idstringyes
siwe_messagestringyes
siwe_signaturestringyes
reported_ipstringno

RegisterResponse:

FieldTypeNotes
identityIdentitynormalized lease identity
hostnamestringfriendly hostname; empty when a client that allows canonical fallback finds the friendly name owned by another identity
canonical_hostnamestringrelay-derived identity-bound hostname used by the SDK
expires_atstringlease expiry
access_tokenstringtoken for renew, unregister, signer access, and direct datagram backhaul
reverse_endpointReverseEndpointURL, opaque reverse-only capability, and expiry for /sdk/connect
udp_addr, tcp_addrstringomitted when transport is disabled
udp_enabled, tcp_enabledbooleanactive transport flags

The SDK uses the returned canonical_hostname; it does not derive or accept a client-supplied address suffix. The canonical label ends with all 40 lowercase hexadecimal characters of the SIWE-authenticated Ethereum address. A normalized name of at most 22 characters precedes it with one hyphen (<name>-<address>); a longer name is replaced by a 21-character hash of the name followed by two hyphens (<name-hash>--<address>, derivation in the architecture page). The current SDK allows canonical fallback. RelayStatus.PublicURL reports the canonical hostname. RelayStatus.AliasURL reports the returned hostname when the relay granted the friendly alias, and is empty after canonical fallback or for an unnamed service. Both stay set while the lease is held, including while reverse sessions reconnect; a failed relay clears AliasURL. Show it to users only while the relay is ready. A protocol-10 client that omits the capability receives hostname_conflict instead of a canonical-only registration when its friendly hostname is already owned by another identity.

ReverseEndpoint:

FieldTypeNotes
urlstringHTTPS /sdk/connect endpoint on the ingress relay or its selected gateway
capabilitystringopaque, reverse-only, and bound to this lease instance
expires_atstringnever later than the owning lease expiry
overlaybooleantrue when the selected endpoint uses the overlay
gatewayRelayDescriptorsigned descriptor binding the overlay gateway’s HTTPS address to its relay identity; required when overlay is true

The reverse capability is not accepted by renew, unregister, signer, or datagram endpoints. The lease access_token is not accepted by the reverse endpoint.

The relay preserves the overlay preference for the lease lifetime. By default it issues the ingress relay’s direct endpoint. When overlay is true, it prefers an available overlay gateway and falls back to the direct endpoint. Both modes intentionally preserve the same SDK-facing contract: use the returned url and reverse-only capability to open the stream. Portal owns endpoint selection, identity, lease policy, and authorization; IVNP owns the network path between gateway and ingress. The SDK does not choose IVNP routers or receive their topology, and that topology is not part of leases or discovery state. For overlay endpoints, the SDK verifies the signed gateway descriptor and checks that its address matches the returned URL before opening any connection. Relay-selected gateways may only resolve to public IP addresses; local, private, and special-purpose addresses are blocked after DNS resolution on every dial. An explicitly configured direct relay retains local development support. Browser/WASM clients reject relay-selected alternate gateways because the browser does not expose resolved addresses for SDK verification. Keep overlay disabled and use the explicitly configured direct relay in browser applications.

See IVNP-backed overlay networking for the conceptual model and the architecture for the delegated capability and gateway-to-ingress protocol flow.

Renew And Unregister

RenewRequest:

FieldTypeRequired
access_tokenstringyes
ttlnumberno
reported_ipstringno
metadataLeaseMetadatano

Renewal uses the same 120-second default and 600-second maximum as registration. A rejected TTL leaves the existing lease unchanged.

RenewResponse:

FieldType
expires_atstring
access_tokenstring
reverse_endpointReverseEndpoint

POST /sdk/reverse rotates only the reverse endpoint. It does not renew or replace the lease. The request contains access_token and may include the generic failed_url; the latter lets the ingress avoid the failed endpoint when another gateway or the direct path is available.

UnregisterRequest:

FieldTypeRequired
access_tokenstringyes

/sdk/unregister returns {} on success.

Static Cache

All /sdk/cache operations require X-Portal-Access-Token: <lease access_token> for an active, cache-opted-in lease. The reverse capability cannot authorize them. Cache capability is optional; clients retain the live origin tunnel if admission or upload fails.

StaticCacheLimits (the optional /sdk/domain cache object):

FieldTypeMeaning
max_exposure_bytesnumberTotal file bytes allowed for one snapshot
max_object_sizenumberMaximum bytes for one file

StaticCacheManifest:

{
  "index": "index.html",
  "files": [
    { "path": "index.html", "size": 5, "sha256": "<64-character SHA-256 hex digest>" }
  ]
}

Paths must be safe, relative, unique, and sorted, and index must identify a listed file. Only regular files are eligible. Manifests are limited to 1 MiB and 2,048 files, in addition to the advertised byte limits.

  • POST: send the manifest as JSON. present indicates whether the matching unexpired snapshot exists. A changed manifest invalidates the previous snapshot.
  • PUT: send multipart/form-data, with the JSON manifest as the first part (manifest), followed by one binary part (object) per file, in manifest order. Sizes and hashes are validated before atomic publication.
  • DELETE: invalidate this lease’s snapshot; returns present: false.

Successful operations return the standard envelope containing StaticCacheStatus: present (boolean) and expires_at (timestamp when a snapshot is present). Authentication, expiry, capacity, and method failures must be handled by HTTP status; not every cache error uses the JSON envelope. See cache configuration for TTL and storage limits and the TLS boundary.

Reverse Connect

GET /sdk/connect opens a native reverse tunnel stream or establishes the browser reverse multiplexer.

Requirements:

RequirementValue
HTTP versionHTTP/1.1
Native authenticationX-Portal-Reverse-Capability: <opaque capability> header
Native connectionkeep-alive capable connection that supports hijack
Browser authenticationportal.reverse.v1 and the opaque capability offered as WebSocket subprotocols
Browser connectionWebSocket carrying a yamux session; each logical stream presents the current capability again

For a native connection, the relay writes HTTP/1.1 101 Switching Protocols and hijacks the TCP connection. There is no JSON response body. For a browser connection, it completes the WebSocket handshake and accepts reverse connections as yamux streams. Failures before either upgrade use the standard JSON error envelope.

The SDK keeps several ready reverse streams open. When an end user connects to the lease hostname, the relay claims one ready stream and bridges encrypted tenant bytes between the browser side and the SDK side.

Browser/WASM SDK Runtime

The same sdk.Expose and sdk.RunHTTP lifecycle can run in a Go WebAssembly application. The application supplies the HTTP handler; the browser runtime is not a general-purpose bridge to a local port or file.

id, err := identity.Generate("browser-preview")
if err != nil {
    return err
}

exposure, err := sdk.Expose(ctx, id, []string{relayURL})
if err != nil {
    return err
}
defer exposure.Close()

handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    _, _ = io.WriteString(w, renderCurrentPreview())
})
return sdk.RunHTTP(ctx, exposure, handler, "")

Compile the application with GOOS=js GOARCH=wasm. The SDK detects the JS runtime and uses WebSocket plus yamux for reverse connections; callers do not select that transport through an option.

A relay exposes the maintained connector and matching Go runtime glue at /api/install/bin/js-wasm and /api/install/bin/wasm-exec. Use the release_version returned by /sdk/domain as their version query parameter so the relay rejects an incompatible cached artifact.

Browser runtime limits:

  • HTTPS handler exposure only
  • no raw TCP or UDP exposure
  • no overlay transport
  • no native MITM self-probe or WithMITMProtection(true)
  • lifetime bound to the page and application context

The repository’s cmd/portal-tunnel/wasm command is the maintained JS interop reference. See Browser reverse transport for the carrier protocol and Browser/WASM transport for its trust boundary.