API reference

Tunnel

Device grants, tunnel tickets of at most 30 seconds, one-time browser links and the client side of the tunnel.

POST/v1/dev/sessions/{session_id}/tickets

Create a tunnel ticket

Mint a tunnel ticket of at most 30 s for a granted device, signed by that device, at the session's current epoch.

The owner, or a member with a live grant, else 403 not_allowed; a member's answer carries its role.

Permission: dev:sessions.attach

Parameters

NameTypeRequiredDescription
session_idpathstringRequiredThe session's ID.
Ciresk-TunnelheaderstringOptionalthe tunnel protocol the client speaks. With 2, the ticket is keyed (its signed claims carry tunnel_pin, the worker's per-epoch certificate pin) when the installation turned end to end on, the session's worker reported its pin for the epoch and every polling relay announced v2; a keyed ticket's streams are TLS 1.3 end to end and the worker refuses a v1 open on it. Absent or 1: an unkeyed ticket

Request body

device_public_keystringRequired
ed25519 public key
device_signaturestringRequired
ed25519 over dev-ticket:<session>:<signed_at>, base64url
signed_atintegerRequired
unix seconds

Response201 Created

epochintegerRequired
expires_atstring (date-time)Required
kindsarray of stringsRequired
stream kinds and capabilities, minted from the caller's role and the policy
relay_urlstring (uri)Required
ssh_host_keystring or null
the epoch's openssh host key once the worker reported it; a renewal refreshes known_hosts
syncbooleanRequired
true only for the creating device
ticketstringRequired
ticket_idstringRequired

Errors

  • 403device_not_granted
  • 409session_not_running
curl -X POST "$CIRESK_API/v1/dev/sessions/devs_0166ba8e973a3df1b173b30df1cb8f2f/tickets" \
  -H "Authorization: Bearer $CIRESK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "device_public_key": "q3Vt1kYwZ8Xo2Jc9bN4rTe7LmA0sPfHgD6iUyW5xCzE",
    "device_signature": "Z2Jm0X4pR8sQk1vT7wYc3nL6eHa9uD2fB5gJiM0oKtN7rPqS1xVyW4zA8cE6lU3dG9hF2jI5kO7mQ0nR4sT1uV",
    "signed_at": 1
  }'
POST/v1/dev/sessions/{session_id}/grants

Grant a device

Grant a device access to the session's tunnel (the creating device is granted at create).

The owner, or a member with a live grant, else 403 not_allowed. An attached device may not open sync.

Permission: dev:sessions.attach

Parameters

NameTypeRequiredDescription
session_idpathstringRequiredThe session's ID.

Request body

device_namestring
device_public_keystringRequired

Response201 Created

created_atstring (date-time)Required
idstringRequired
principalstringRequired
session_idstringRequired

Errors

curl -X POST "$CIRESK_API/v1/dev/sessions/devs_0166ba8e973a3df1b173b30df1cb8f2f/grants" \
  -H "Authorization: Bearer $CIRESK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "device_name": "maya-laptop",
    "device_public_key": "q3Vt1kYwZ8Xo2Jc9bN4rTe7LmA0sPfHgD6iUyW5xCzE"
  }'
DELETE/v1/dev/sessions/{session_id}/grants/{grant_id}

Revoke a device grant

Revoke a device grant; the relay closes that device's connections at once.

Owner only, or a principal with dev:sessions.kill (audited).

Permission: dev:sessions.attach

Parameters

NameTypeRequiredDescription
session_idpathstringRequiredThe session's ID.
grant_idpathstringRequiredThe grant's ID.

Response204 No Content

revoked

Errors

curl -X DELETE "$CIRESK_API/v1/dev/sessions/devs_0166ba8e973a3df1b173b30df1cb8f2f/grants/devg_01d7f828cd2d8a17550e8d59d6319b9f" \
  -H "Authorization: Bearer $CIRESK_KEY"
POST/v1/dev/sessions/{session_id}/web_handoff

Open a session in the browser

A one-time, 60-second link that opens one http port, the diff, the terminal or VS Code Web of the session in the browser.

The owner, or a member whose grant passes its re-check, else 403 not_allowed; a person only. The answer's url is on the gateway's preview host (<target>--<label>--<session>.<cell>.<preview-domain>) and carries a dvh_ handoff, valid once and for 60 s, bound to session, epoch, target, principal, member and role, which ciresk-dev-gateway redeems with dev.gateway.redeem. The target is an http port, diff, term (the session's shell through the gateway's ssh client: ssh for owner and use under web use when their kinds hold ssh, else ssh_ro when they hold it; ssh deny refuses it) or ide (VS Code Web: ide for owner and use under web use, else ide_ro). Refused 403 web_off when the organisation's policy has web off, 403 ssh_off for term when ssh is denied, 403 web_ide_off for ide when web_ide is off, 409 session_not_running when the session is not placed, 422 target_not_exposed when the port is not an http port of the session, 503 web_unavailable when the installation has no preview domain.

Permission: dev:sessions.attach

Parameters

NameTypeRequiredDescription
session_idpathstringRequiredThe session's ID.

Request body

targetstringRequired
an http port of the session's expose in decimal, diff, term (the terminal) or ide (VS Code Web)

Response201 Created

expires_atstring (date-time)Required
60 s after the call; the handoff is valid once
urlstring (uri)Required
https://<target>--<label>--<session>.<cell>.<preview-domain>/__ciresk/gateway/redeem?handoff=dvh_...

Errors

  • 403not_allowed, web_off, ssh_off, web_ide_off
  • 409session_not_running, access_changed
  • 422target_not_exposed
  • 429rate_limited: more than 30 links a minute; retry_after and the Retry-After header say the seconds until the minute's oldest link leaves the window
  • 503web_unavailable
curl -X POST "$CIRESK_API/v1/dev/sessions/devs_0166ba8e973a3df1b173b30df1cb8f2f/web_handoff" \
  -H "Authorization: Bearer $CIRESK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "target": "3000"
  }'
GET/v1/dev/tunnel/client

Open the client tunnel

The developer side of the tunnel (WebSocket upgrade, a nonce signed by the device key, then yamux).

Authorization carries a dvt_ ticket. Protocol The relay answers every websocket upgrade with Ciresk-Tunnel 1 with 101 and checks the ticket after it, in band: a refusal is a {"refused": {"code"}} message in place of the nonce or of the ok, then the close, with code invalid_ticket (the ticket does not verify or names another session or grant), stale_epoch, killed, revoked, feed_stale (the relay's feed is silent and it fails closed), draining (the relay is restarting), invalid_device (the device did not prove its key over the relay's nonce) or limit_exceeded. Every stream the worker verifies, ping excepted, is counted in the worker's per-minute streams window, audited as dev.tunnel.connect when the window opened a stream, and read with dev.connections.list.

Permission: dev:sessions.attach

Parameters

NameTypeRequiredDescription
Ciresk-TunnelheaderstringRequired

Response101 Switching Protocols

switching protocols; the ticket is checked in band after it

Errors

  • 400not_a_tunnel_upgrade: not a websocket upgrade, or without Ciresk-Tunnel: 1
curl "$CIRESK_API/v1/dev/tunnel/client" \
  -H "Authorization: Bearer $CIRESK_KEY" \
  -H "Ciresk-Tunnel: acme/platform"