Tether
tether marketplace package (tibi plugin add tether) connects pages to a Tether project: window.Tibi.tether and reactive helpers in the browser, and a privileged ctx.tether in frontmatter and API routes. It follows @tthr/client 0.4.Configure
The signed-in user's token comes from Tibi's built-in sign-in by default (Auth): the Strands Accounts id_token, which Tether verifies when the project has a Strands auth provider (issuer https://sessions.strands.gg, audience = your client id). Set TIBI_TETHER_AUTH_MODE to localstorage or cookie (with TIBI_TETHER_AUTH_KEY) to read it from somewhere else, or call Tibi.tether.configureAuth({ mode: 'callback', callback }). On a 401 the client asks for a fresh token once and retries.
In components
useQuery(name, args, { live, enabled })returns{ data, loading, error, refetch }signals and stays live over the WebSocket unlesslive: false. Responses to superseded args are ignored.useMutation(name, { invalidates })returns{ mutate, data, loading, error, reset };useSubscription(name, args, callback)returns{ data }. All three clean up when the component unmounts.- Imperative:
query,mutation(name, args, { invalidates }),subscribe(name, args, callback, onError?)(the first result arrives over HTTP straight away, then changes over the socket),invalidateQueries,storage.upload / getUrl / getMetadata / delete / list. - Failures carrying a code (
{ error, code, data }, such as a transactional mutation'sCONFLICT) throwTibi.tether.TetherErrorwith.codeand.data;instanceofalso matches errors from@tthr/client. - The user token travels as a WebSocket subprotocol, never in the URL. Reconnects never give up (jittered backoff up to 30 seconds) and catch up on missed changes.
On the server
ctx.tether (query, mutation, bridge.query, bridge.mutate) is available in JS / TS frontmatter and API routes whenever TIBI_TETHER_URL and TIBI_TETHER_PROJECT_ID are set; it authenticates with TIBI_TETHER_API_KEY. An API route that lets a TetherError escape answers with a matching status (CONFLICT 409, NOT_FOUND 404, FORBIDDEN 403, …) and { error, code, data }.
Bridges
Calls into a bridged project need a secret API key, so they only happen server-side: ctx.tether.bridge.query(alias, fn, args) and ctx.tether.bridge.mutate(alias, fn, args) in frontmatter and API routes, or ctx.bridge inside your Tether functions. Tether has no actions, so there is no exec. In the browser, Tibi.tether.bridge.* rejects straight away with a FORBIDDEN TetherError saying so; call one of your own functions (or an API route) that makes the bridge call instead.
examples/tether in the Tibi repo runs against a small fake Tether (node fake-tether.mjs).