realtime

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

README

realtime-go

Go SDK for the Foony Realtime service. A small client for the wire protocol implemented by services/realtime-saas: connect, subscribe, publish, presence, and history, plus a REST client for backends. Feature parity with @foony/realtime (the TypeScript SDK).

Install

go get github.com/Foony-Limited/realtime-go

The module's only dependency is github.com/coder/websocket.

Quick start

package main

import (
	"context"
	"fmt"
	"os"

	realtime "github.com/Foony-Limited/realtime-go"
)

func main() {
	ctx := context.Background()

	// Initialize the realtime client with your Realtime API key. Go code usually
	// runs on a server you trust, so key auth is the right fit here. (For
	// request/response access without holding a connection open, use
	// realtime.NewRest instead.)
	client, err := realtime.New(realtime.Options{
		Key: os.Getenv("FOONY_REALTIME_API_KEY"),
		// How this client is named in presence and on the messages it publishes.
		ClientID: "quickstart",
	})
	if err != nil {
		panic(err)
	}
	defer client.Close()

	// Open the WebSocket and authenticate. This is optional (the first publish or
	// subscribe connects lazily), but connecting eagerly surfaces a bad key here
	// instead of on your first message.
	if err := client.Connect(ctx); err != nil {
		panic(err)
	}
	fmt.Println("connected to Foony Realtime")

	// Get a reference to the "test-channel" channel. The same name always returns
	// the same instance.
	channel := client.Channels.Get("test-channel")

	// Subscribe to all messages published to this channel. Message.Data is the raw
	// JSON payload. Unmarshal it into your own type as needed.
	received := make(chan struct{})
	channel.Subscribe(func(message *realtime.Message) {
		fmt.Printf("received %s: %s\n", message.Name, message.Data)
		close(received)
	})

	// Publish a test message to the channel. Publish returns once the server has
	// acked it, and the subscription above receives it like any other subscriber.
	if err := channel.Publish(ctx, "test-event", "hello world"); err != nil {
		panic(err)
	}

	// Wait for the delivery before the program exits.
	<-received
}

Building an app you distribute to users? Don't ship the API key in it: construct the client with AuthCallback returning a short-lived JWT fetched from your backend, and have that backend mint the JWT locally with realtime.CreateJWT (it signs with your API key, with no network call) or via rest.Auth.RequestToken. Either way the key stays on your server.

Local development against the realtime backend

Start the backend following services/realtime-saas/README.md. Then mint a dev token:

cd services/realtime-saas
JWT_SIGNING_KEY=local-dev-key go run ./cmd/devtoken -app foony -client alice

Use the printed token in the SDK:

client, err := realtime.New(realtime.Options{
	Endpoint: "ws://localhost:3000",
	Token:    os.Getenv("FOONY_REALTIME_DEV_TOKEN"),
})

Omit Endpoint in production to use wss://realtime.foony.io.

Channel names

Channel names are 1 to 255 ASCII characters from A-Z a-z 0-9 : - _ and cannot start with a :. Use colons to express hierarchy (chat:rooms:42). Dots are not allowed. Channels.Get panics on an invalid name (and the server rejects one with error code 40001, CodeBadFrame).

API surface

  • realtime.New(options): top-level Client. Owns the WebSocket. Channels attach lazily.
  • client.Channels.Get(name): returns a stable *Channel for that name.
  • channel.Subscribe(fn): message listener. Returns an unsubscribe func.
  • channel.Subscribe(fn, "greeting"): message listener for specific message names.
  • channel.On(fn) / channel.OnEvent(realtime.ChannelEvent(realtime.ChannelAttached), fn): channel lifecycle state listeners.
  • channel.Publish(ctx, name, data): publish one message. Returns on ack. Payloads are marshaled with encoding/json, and delivered Message.Data is a json.RawMessage.
  • channel.PublishBatch(ctx, messages): publish an atomic batch (stored, deduped, and billed as one message).
  • channel.History(ctx, params): recent messages, with serial-cursor paging.
  • channel.Presence.Subscribe(fn) / channel.Presence.On(action, fn): presence listeners.
  • channel.Presence.Enter/Update/Leave(ctx, data): mutate this connection's membership.
  • client.Connection.On(fn) / client.Connection.OnState(state, fn) / client.Connection.Once(state, fn): observe connection state.
  • client.BatchPublish(ctx, specs...): publish to up to 100 channels in one call.
  • realtime.CreateJWT(key, params): mint a capability-scoped JWT locally.
  • realtime.NewCipher + realtime.WithCipher: end-to-end encryption (AES-GCM). The edge only ever sees ciphertext.
  • realtime.NewRest(options): HTTP client for publish, history, presence, and token minting without a connection (see REST).

Errors from the service are *realtime.ServerError values carrying the numeric protocol code:

var serverErr *realtime.ServerError
if errors.As(err, &serverErr) && serverErr.Code == realtime.CodeCapability {
	// the token does not grant this action
}

REST

For backends and integrations that publish or read without holding a connection open (cron jobs, serverless functions, webhooks), use the Rest client. It talks to the same service over HTTPS, and its publishes are identical to WebSocket publishes for subscribers, history, and billing.

rest, err := realtime.NewRest(realtime.RestOptions{Key: os.Getenv("REALTIME_API_KEY")})
channel := rest.Channels.Get("chat:room:42")

// Publish one message, or several (stored and delivered as one atomic batch).
_, err = channel.Publish(ctx, "greeting", map[string]string{"text": "hello"})

// History, newest first. Page through older messages with Next.
page, err := channel.History(ctx, realtime.RestHistoryParams{Limit: 100})
for _, message := range page.Items {
	fmt.Println(message.Name, string(message.Data))
}
for page.HasNext() {
	page, err = page.Next(ctx)
}

// Current presence members.
members, err := channel.Presence.Get(ctx, realtime.RestPresenceParams{})

// Mint a client JWT from your API key (for handing to browser clients).
details, err := rest.Auth.RequestToken(ctx, realtime.TokenParams{
	ClientID:   "user-42",
	Capability: realtime.Capability{"chat:*": {"subscribe", "publish"}},
})

Auth accepts the same options as the realtime client: Key (server-side), Token, or AuthCallback (refreshed automatically when the service reports it expired). Channels accept the same WithCipher option for end-to-end encryption. Errors are *realtime.RestError values carrying the numeric protocol Code plus the HTTP StatusCode.

Reconnect

When the connection drops unexpectedly the client retries with exponential backoff (1s, 2s, 4s, ..., capped at 30s). Everything is restored automatically on reconnect: subscriptions are re-issued (with a resume cursor, so missed messages within retention are replayed), presence watchers are re-opened, and whatever presence membership this connection had entered is re-entered. Call Presence.Leave if you no longer want to be present.

Set DisableAutoReconnect: true to disable retries entirely (useful in tests).

Publishes made while the connection is establishing or temporarily down are queued locally and flushed on the next successful (re)connect, so a publish during a brief blip returns nil rather than an error. A publish that was already in flight when the connection dropped is resent on reconnect too. Every publish carries a stable client-assigned id, so the server collapses any duplicate that a resend would otherwise create (exactly-once). Set DisableQueueing: true to disable buffering/resend and fail such publishes immediately.

Concurrency

All exported methods are safe for concurrent use. Listeners run one at a time, in event order, on a dispatcher goroutine owned by the client, so a listener may call blocking SDK methods (like Publish) without deadlocking message delivery. Blocking calls take a context.Context. Canceling it abandons the caller's wait, not the underlying operation.

Tests

go test ./...

Runs wire golden tests (shared byte vectors with the TypeScript SDK and the server, so the codecs cannot drift) plus in-process end-to-end tests that drive the SDK against a fake edge over a real WebSocket. No external services required.

License

Apache-2.0 © Foony Limited

Documentation

Overview

Package realtime is the Go SDK for the Foony Realtime service: the WebSocket Client (a Channels.Get(name) map with per-channel Subscribe / Publish / Presence), the request/response Rest client for backends, CreateJWT for server-side token minting, and the Cipher helpers for end-to-end encryption.

Index

Constants

View Source
const (
	// CodeBadFrame means a malformed or unparseable frame.
	CodeBadFrame = 40001
	// CodeBadAuth means authentication failed (bad token or key).
	CodeBadAuth = 40101
	// CodeAuthExpired means previously valid auth has expired. Re-authenticate.
	CodeAuthExpired = 40102
	// CodeForbidden means authenticated but not permitted for this channel or action.
	CodeForbidden = 40300
	// CodeCapability means the token's capability does not grant the requested action.
	CodeCapability = 40301
	// CodeChannelDenied means the token's capability does not grant access to this
	// specific channel.
	CodeChannelDenied = 40302
	// CodeNotFound means a referenced resource (e.g. a channel) does not exist.
	CodeNotFound = 40400
	// CodeRateLimited means too many requests. The publish or connection rate limit was
	// exceeded.
	CodeRateLimited = 42900
	// CodeServer means an unexpected server-side error.
	CodeServer = 50000
	// CodeBootstrap means the edge could not bootstrap its streams. Retry later.
	CodeBootstrap = 50001
)

Error codes the server uses on error frames, mirrored one-for-one from the server's wire package (the canonical source). Compare against ServerError.Code.

View Source
const DefaultEndpoint = "realtime.foony.io"

DefaultEndpoint is the Foony Realtime endpoint used when Options.Endpoint is empty.

Variables

This section is empty.

Functions

func CreateJWT

func CreateJWT(key string, params CreateJWTParams) (string, error)

CreateJWT signs a JWT locally with key, with no network call. The token's kid header is the public key name ("appSlug.publicKeyId") so the edge can look up the secret to verify it. The payload carries only the subject, capability, and expiry, and no secret. It returns the compact "header.payload.signature" string, and an error when the key is missing or malformed, or when TTL is negative.

// In your token endpoint. The key stays server-side.
token, err := realtime.CreateJWT(os.Getenv("FOONY_API_KEY"), realtime.CreateJWTParams{
	ClientID:   userID,
	Capability: realtime.Capability{"chat:" + userID + ":*": {"subscribe", "publish"}},
})

func GenerateRandomKey

func GenerateRandomKey(bits int) (string, error)

GenerateRandomKey generates a random base64-encoded AES key. Share the returned string between the clients that should be able to read a channel. Never send this to our backend. bits is the key size: 128 or 256 (use 256 unless you have a reason not to).

func IsCipherEncoding

func IsCipherEncoding(encoding string) bool

IsCipherEncoding reports whether encoding indicates a ciphered payload that needs a Cipher to read.

Types

type Auth

type Auth struct {
	// contains filtered or unexported fields
}

Auth is the token-minting namespace on a Client. It signs with the client's key. See https://foony.io/docs/auth for when to mint tokens yourself.

func (*Auth) CreateJWT

func (a *Auth) CreateJWT(params CreateJWTParams) (string, error)

CreateJWT mints a short-lived JWT scoped to params.Capability, signed with the client's API key. This is local, with no network call. It returns an error when the client was not constructed with a key. Use the package-level CreateJWT to sign with an explicit key.

type BatchChannelResult

type BatchChannelResult struct {
	// Channel is the channel this result is for.
	Channel string
	// Err is set when this channel's publish failed.
	Err error
}

BatchChannelResult is one channel's outcome from Client.BatchPublish.

type BatchMessage

type BatchMessage struct {
	// Name is the application-level event name.
	Name string
	// Data is the JSON-serializable payload.
	Data any
}

BatchMessage is one message in a batch publish: its own event name and payload.

type BatchOptions

type BatchOptions struct {
	// Interval is the minimum gap between batch sends, applied as a throttle. A
	// publish is sent right away unless a batch went out within the last Interval, in
	// which case it waits until the window is up. Publishes spaced further apart than
	// Interval are never batched together and add no latency. Only fast bursts get
	// grouped into one batch. Defaults to 10ms when zero.
	Interval time.Duration
	// MaxMessages flushes early once this many messages are buffered. Defaults to 200
	// when zero.
	MaxMessages int
}

BatchOptions configures automatic publish batching. Single Publish calls are always auto-batched, buffered and flushed as one batch frame (one stored, dedupable message), which massively raises per-channel throughput for little to no latency cost. Batching is always on. PublishBatch and Client.BatchPublish are never batched further (they assume the caller is managing batching).

type BatchPublishResult

type BatchPublishResult struct {
	// SuccessCount is the number of channels published successfully.
	SuccessCount int
	// FailureCount is the number of channels that failed to publish.
	FailureCount int
	// Results has one entry per channel. Err is set when that channel's publish
	// failed.
	Results []BatchChannelResult
}

BatchPublishResult is the per-channel result set from Client.BatchPublish.

type BatchSpec

type BatchSpec struct {
	// Channels are the channel names to which Messages will be published.
	Channels []string
	// Messages are published to every channel in Channels.
	Messages []BatchMessage
}

BatchSpec is one batch-publish spec: send Messages to each of Channels.

type Capability

type Capability map[string][]string

Capability maps a channel pattern to its allowed operations (e.g. {"chat:site:*": {"subscribe", "publish"}}).

type Channel

type Channel struct {
	// Name is the channel name this instance is bound to (e.g. "chat:room-1").
	Name string
	// Presence announces membership on this channel and listens on who comes and goes.
	Presence *Presence
	// contains filtered or unexported fields
}

Channel is a named channel. Subscribe to receive its messages, publish to send them, and use Channel.Presence to see who is there. Get instances via client.Channels.Get(name). The same name always returns the same instance on a given client.

The channel has two separate listener surfaces: On / Once / Off listen on the channel's lifecycle ChannelState, while Subscribe / Unsubscribe receive application messages.

channel := client.Channels.Get("chat:room-1")
channel.Subscribe(func(message *realtime.Message) {
	fmt.Println(message.Name, string(message.Data))
}, "greeting")
err := channel.Publish(ctx, "greeting", map[string]string{"text": "hi"})

func (*Channel) Attach

func (ch *Channel) Attach(ctx context.Context) error

Attach ensures the server is subscribed to this channel. Subscribe and the presence methods call this implicitly, so calling it yourself is optional. It is useful for surfacing attach errors before the first message arrives. It returns nil once the server confirms the channel subscription, the server's error when the token lacks the subscribe capability (the channel moves to ChannelFailed and is not retried), and the transport error when the request fails in transit (the channel moves to ChannelSuspended and re-attaches on reconnect).

func (*Channel) Detach

func (ch *Channel) Detach(ctx context.Context) error

Detach detaches from the server: stop receiving messages and presence events. Buffered auto-batched publishes are flushed first. Local listeners are preserved, call Off or Unsubscribe to clear them. It returns nil once the server confirms the detach, and the request's error when it fails, though the channel is marked detached either way.

func (*Channel) Flush

func (ch *Channel) Flush()

Flush sends any buffered (auto-batched) publishes now, as a single batch frame. This runs automatically once the throttle window elapses, when the buffer is full, and on Detach. Call it to force an immediate send. A no-op when nothing is buffered.

func (*Channel) History

func (ch *Channel) History(ctx context.Context, params HistoryParams) (*HistoryResult, error)

History fetches recent messages for this channel, oldest first. History is a one-shot read and does not interleave with the live subscription. How far back it reaches depends on each message's retention, see https://foony.io/docs/history. On a channel with a cipher, messages are decrypted before they are returned. It returns the server's error when history cannot be read, for example a missing history capability.

func (*Channel) Off

func (ch *Channel) Off()

Off removes every channel state listener.

func (*Channel) On

func (ch *Channel) On(listener func(ChannelStateChange)) func()

On registers a listener for every channel state event and returns its unsubscribe function.

func (*Channel) OnEvent

func (ch *Channel) OnEvent(event ChannelEvent, listener func(ChannelStateChange)) func()

OnEvent registers a listener for one ChannelEvent (a state name or ChannelEventUpdate) and returns its unsubscribe function.

func (*Channel) Once

func (ch *Channel) Once(event ChannelEvent, listener func(ChannelStateChange)) func()

Once registers a listener invoked one time for the next matching event, and returns its unsubscribe function.

func (*Channel) Publish

func (ch *Channel) Publish(ctx context.Context, name string, data any, options ...PublishOption) error

Publish publishes one message to the channel. On a channel with a cipher, data is end-to-end encrypted before it is sent. data is marshaled with json.Marshal (pass a json.RawMessage to send pre-encoded JSON). It returns nil once the server acks the publish. Unless Options.DisableQueueing is set, publishes made while the connection is down are queued locally and sent on reconnect. It returns the server's error when the service refuses the publish (for example a token without the publish capability), and an immediate error when the connection is closing, closed, or failed. With DisableQueueing, any connection state but connected returns an error immediately.

Pass WithEphemeral to send a fire-and-forget message: delivered live to current subscribers but never stored, so it is excluded from history and reconnect replay.

func (*Channel) PublishBatch

func (ch *Channel) PublishBatch(ctx context.Context, messages []BatchMessage, options ...PublishOption) error

PublishBatch publishes a batch of messages in a single frame under one message id. This is an atomic batch. The server stores and dedups it as one durable message, while subscribers receive the members individually. This counts as 1 message for the purposes of usage limits / quotas (message size limits still apply). Pass WithEphemeral to make the whole batch fire-and-forget.

func (*Channel) State

func (ch *Channel) State() ChannelState

State returns the current ChannelState. Listen on changes with Channel.On.

func (*Channel) Subscribe

func (ch *Channel) Subscribe(listener func(*Message), names ...string) func()

Subscribe registers a listener for messages on this channel: every message when no names are given, otherwise only messages whose name matches one of names. It implicitly attaches if needed and returns an unsubscribe function. Subscribe itself does not block, so attach failures do not surface here: call Channel.Attach first if you want to observe them.

func (*Channel) Unsubscribe

func (ch *Channel) Unsubscribe()

Unsubscribe removes every message listener on this channel. To remove one listener, call the function Subscribe returned.

type ChannelEvent

type ChannelEvent string

ChannelEvent is an event name channel state listeners can filter on: every ChannelState plus ChannelEventUpdate.

const ChannelEventUpdate ChannelEvent = "update"

ChannelEventUpdate is a change that is not a state transition. The channel stayed in the same state but something was updated, for example the server reported a resume outcome while the channel was already attached. Check Resumed on the payload: false means messages may have been missed beyond retention, so reload state or read history.

type ChannelOption

type ChannelOption func(*channelSettings)

ChannelOption customizes a channel on its first Channels.Get.

func WithBatchOptions

func WithBatchOptions(batch BatchOptions) ChannelOption

WithBatchOptions tunes the channel's auto-batching, overriding the Options.Batch default.

func WithCipher

func WithCipher(cipher *Cipher) ChannelOption

WithCipher enables end-to-end payload encryption on the channel with the given Cipher. This prevents the Foony backend from seeing the plaintext data of messages published to this channel. The cipher key should be kept private and never shared with the public or our backend.

type ChannelState

type ChannelState string

ChannelState is a channel lifecycle state. A healthy channel follows the states in order: initialized -> attaching -> attached -> detaching -> detached -> attaching, and so on. A channel in the failed state is not retried and is not re-attached.

const (
	// ChannelInitialized means the channel was created locally and no attach has been
	// attempted yet.
	ChannelInitialized ChannelState = "initialized"
	// ChannelAttaching means an attach was requested and is awaiting server
	// confirmation.
	ChannelAttaching ChannelState = "attaching"
	// ChannelAttached means messages and presence for this channel are flowing.
	ChannelAttached ChannelState = "attached"
	// ChannelDetaching means a detach was requested and is awaiting server
	// confirmation.
	ChannelDetaching ChannelState = "detaching"
	// ChannelDetached means no messages or presence are delivered until re-attached.
	ChannelDetached ChannelState = "detached"
	// ChannelSuspended means the channel was temporarily lost, for example because the
	// connection dropped. The SDK re-attaches on reconnect. You can keep publishing:
	// unless Options.DisableQueueing is set, publishes are queued locally and sent
	// once reconnected.
	ChannelSuspended ChannelState = "suspended"
	// ChannelFailed means the attach failed with an error a retry will not fix, for
	// example a missing capability, and the SDK will not retry it. Call Attach to try
	// again manually, for example after obtaining a token with more capabilities.
	ChannelFailed ChannelState = "failed"
)

Channel lifecycle states.

type ChannelStateChange

type ChannelStateChange struct {
	// Current is the state the channel is now in.
	Current ChannelState
	// Previous is the state the channel was in immediately before this event.
	Previous ChannelState
	// Reason is the error that caused the transition, when the event was error-driven.
	Reason error
	// Resumed is true when the channel resumed without missing messages (e.g. after a
	// reconnect).
	Resumed bool
}

ChannelStateChange is the payload delivered to channel state listeners.

type Channels

type Channels struct {
	// contains filtered or unexported fields
}

Channels is the channel registry of one Client: Get returns the stable instance for a name, Release removes it.

func (*Channels) Get

func (chans *Channels) Get(name string, options ...ChannelOption) *Channel

Get returns the Channel named name, creating it on first use. The same name always returns the same instance. name is 1 to 255 characters from "A-Z a-z 0-9 : - _" and may not start with a ':'. Colons express hierarchy (e.g. "chat:rooms:42"), dots are not allowed. Get panics on an invalid name: the server's grammar is enforced client-side so a bad name fails loudly here instead of attach-looping against BadFrame rejections.

Options (e.g. WithCipher) apply when the channel is first created. Passing different options to a later Get of the same name returns the existing instance unchanged.

func (*Channels) Release

func (chans *Channels) Release(name string)

Release releases the channel named name. The channel is detached and removed from the client, so a later Get of the same name returns a fresh instance. A no-op when no channel with that name exists.

type Cipher

type Cipher struct {
	// contains filtered or unexported fields
}

Cipher is an AES-GCM cipher for one channel. It encrypts a JSON-serializable value into an (encoding, data) pair and back.

func NewCipher

func NewCipher(params CipherParams) (*Cipher, error)

NewCipher builds a Cipher from params. It returns an error when the key is not 16 or 32 bytes, or when CipherParams.Algorithm contradicts the key length (a caller asking for AES-256 with a 16-byte key must not silently get AES-128).

func (*Cipher) Decrypt

func (c *Cipher) Decrypt(encoding string, data json.RawMessage) (json.RawMessage, error)

Decrypt decrypts a data payload carried under encoding back to its plaintext JSON. data is the raw JSON value from the wire (a base64 JSON string). It returns an error on a bad key, tampering, or an unsupported encoding.

func (*Cipher) Encrypt

func (c *Cipher) Encrypt(value any) (*EncryptResult, error)

Encrypt encrypts a JSON-serializable value with a fresh random IV and returns the (encoding, data) pair to put on the wire.

type CipherAlgorithm

type CipherAlgorithm string

CipherAlgorithm is the algorithm label accepted in CipherParams. The key length picks 128 vs 256.

const (
	// AES256GCM is AES-256-GCM (a 32-byte key).
	AES256GCM CipherAlgorithm = "aes-256-gcm"
	// AES128GCM is AES-128-GCM (a 16-byte key).
	AES128GCM CipherAlgorithm = "aes-128-gcm"
)

The supported cipher algorithms.

type CipherParams

type CipherParams struct {
	// Key is the secret key as raw bytes: 16 (AES-128) or 32 (AES-256). Exactly one of
	// Key and KeyBase64 must be set.
	Key []byte
	// KeyBase64 is the secret key as a base64 string, as produced by
	// [GenerateRandomKey]. Exactly one of Key and KeyBase64 must be set.
	KeyBase64 string
	// Algorithm optionally declares the intended strength. The key length is what
	// actually selects AES-128 vs AES-256, and [NewCipher] returns an error when this
	// label contradicts it.
	Algorithm CipherAlgorithm
}

CipherParams are the parameters for channel encryption. Pass the built Cipher to Channels.Get with WithCipher. The key should be kept private and never shared with the public or our backend.

type Client

type Client struct {
	// Connection is the underlying transport. Listen on lifecycle state with
	// Connection.On and read it with Connection.State.
	Connection *Connection
	// Auth is the token-minting namespace. It signs with the client's key. See
	// https://foony.io/docs/auth for when to mint tokens yourself.
	Auth *Auth
	// Channels is the map-like accessor for channels: a stable instance per name.
	Channels *Channels
}

Client is the realtime client and the entry point for app code. It owns one WebSocket Connection (opened lazily on first use) and a map of Channel instances retrieved via Channels.Get(name). See https://foony.io/docs/getting-started for the auth options and a full walkthrough.

// Prefer AuthCallback over Key for apps distributed to users.
client, err := realtime.New(realtime.Options{
	AuthCallback: func(ctx context.Context) (string, error) {
		return fetchTokenFromYourServer(ctx)
	},
})
channel := client.Channels.Get("chat:room-1")
channel.Subscribe(func(message *realtime.Message) {
	fmt.Println(message.Name, string(message.Data))
})
err = channel.Publish(ctx, "greeting", map[string]string{"text": "hi"})

func New

func New(options Options) (*Client, error)

New builds a Client from options. Exactly one of Options.Key, Options.Token, or Options.AuthCallback must be set, or New returns an error. The connection is opened lazily on first use. Call Client.Connect to open it eagerly.

func (*Client) BatchPublish

func (c *Client) BatchPublish(ctx context.Context, specs ...BatchSpec) (*BatchPublishResult, error)

BatchPublish publishes messages to many channels in one call. Each BatchSpec sends its Messages to each of its Channels, and all messages to a single channel go as one idempotent batch frame. BatchPublish is publish-only and does not handle end-to-end encryption. If you need that, use Channel.Publish (which is the preferred method of publishing all messages).

A BatchPublish is limited to at most 100 distinct channels per call and at most 1000 messages per channel. It returns an error before sending anything when a limit is exceeded. Otherwise it returns a BatchPublishResult: a channel that fails shows up there as an Err entry rather than failing the call, so one channel failing does not fail the others.

func (*Client) Close

func (c *Client) Close()

Close closes the WebSocket and releases every channel. The connection is closed when it returns. Publishes still awaiting an ack fail with a "connection closed" error.

func (*Client) Connect

func (c *Client) Connect(ctx context.Context) error

Connect eagerly opens the WebSocket and completes the auth handshake. Calling this is optional: channels connect and attach lazily on first use. Connect is idempotent, and concurrent calls wait on the same in-flight attempt. It returns nil once the connection is connected, and the handshake error when auth fails (for example a bad key, or an expired static Token with no AuthCallback to re-mint one).

type Connection

type Connection struct {
	// contains filtered or unexported fields
}

Connection is the transport layer. One Client owns one Connection and all of its channels share it. Listen on lifecycle changes with Connection.On, which delivers every ConnectionState transition.

func (*Connection) ClientID

func (c *Connection) ClientID() string

ClientID returns the client id this connection is authenticated as, or "" before the auth handshake completes. Never "" once connected: the server resolves it from the JWT's subject (Token and AuthCallback auth), from Options.ClientID (key auth), or assigns one when neither names a client.

func (*Connection) Close

func (c *Connection) Close()

Close closes the WebSocket and releases resources. The connection is closed when it returns. Requests and publishes still awaiting an ack are rejected with a "connection closed" error.

func (*Connection) Connect

func (c *Connection) Connect(ctx context.Context) error

Connect opens the WebSocket and completes the auth handshake. Connect is idempotent, and concurrent calls wait on the same in-flight attempt (ctx cancels the caller's wait, not the shared attempt). It returns nil once the connection is connected, and the handshake error when auth fails, for example a bad key or an expired static Token with no AuthCallback to re-mint one.

func (*Connection) ID

func (c *Connection) ID() string

ID returns the server-issued connection id, or "" before the auth handshake completes.

func (*Connection) Off

func (c *Connection) Off()

Off removes every connection state listener.

func (*Connection) On

func (c *Connection) On(listener func(ConnectionStateChange)) func()

On registers a listener for every connection state change and returns its unsubscribe function.

func (*Connection) OnState

func (c *Connection) OnState(state ConnectionState, listener func(ConnectionStateChange)) func()

OnState registers a listener for changes into one state and returns its unsubscribe function.

func (*Connection) Once

func (c *Connection) Once(state ConnectionState, listener func(ConnectionStateChange)) func()

Once registers a listener invoked one time for the next change into state, and returns its unsubscribe function.

func (*Connection) State

func (c *Connection) State() ConnectionState

State returns the current ConnectionState. Listen on changes with Connection.On.

type ConnectionState

type ConnectionState string

ConnectionState is a connection lifecycle state. Observe transitions with Connection.On.

const (
	// ConnectionInitialized means the connection was created locally and no connect has
	// been attempted yet.
	ConnectionInitialized ConnectionState = "initialized"
	// ConnectionConnecting means the WebSocket is opening and the auth handshake is in
	// flight. Publishes made now are queued unless Options.DisableQueueing is set.
	ConnectionConnecting ConnectionState = "connecting"
	// ConnectionConnected means connected and authenticated. Messages flow, and
	// [Connection.ID] and [Connection.ClientID] are populated.
	ConnectionConnected ConnectionState = "connected"
	// ConnectionDisconnected means the connection dropped unexpectedly. The state
	// change's Reason says why. Unless Options.DisableAutoReconnect is set, the SDK
	// retries with exponential backoff, starting at Options.InitialReconnectDelay
	// (1 second) and doubling up to Options.MaxReconnectDelay (30 seconds). You can
	// keep publishing: unless Options.DisableQueueing is set, publishes queue locally
	// and are sent on reconnect, and channels re-attach and replay the messages they
	// missed (within retention).
	ConnectionDisconnected ConnectionState = "disconnected"
	// ConnectionClosing means Close was called and the socket is shutting down.
	ConnectionClosing ConnectionState = "closing"
	// ConnectionClosed means the connection was closed by Close. Publishes that were
	// awaiting an ack have been rejected.
	ConnectionClosed ConnectionState = "closed"
	// ConnectionFailed means a failure the SDK will not retry on its own, for example a
	// bad or expired credential with no AuthCallback to re-mint one. The state change's
	// Reason carries the error. An explicit Connect starts a fresh attempt.
	ConnectionFailed ConnectionState = "failed"
)

Connection lifecycle states.

type ConnectionStateChange

type ConnectionStateChange struct {
	// Current is the state the connection is now in.
	Current ConnectionState
	// Reason is the error that caused the transition, when the event was error-driven.
	Reason error
}

ConnectionStateChange is the payload delivered to connection state listeners.

type CreateJWTParams

type CreateJWTParams struct {
	// Capability is the capability the token grants (e.g.
	// {"chat:site:*": {"subscribe"}}). Must be a subset of the signing key's own
	// capability or the edge rejects it on connect. Exactly one of Capability and
	// CapabilityJSON must be set.
	Capability Capability
	// CapabilityJSON is the capability as a pre-serialized JSON string, as an
	// alternative to Capability.
	CapabilityJSON string
	// ClientID identifies the end user the token represents. Echoed back as the
	// connection's client id.
	ClientID string
	// TTL is the token lifetime. Defaults to one hour when zero, short enough to bound
	// a leaked token.
	TTL time.Duration
}

CreateJWTParams describe the token to mint.

type EncryptResult

type EncryptResult struct {
	// Encoding is the transport encoding describing Data, e.g.
	// "cipher+aes-256-gcm/base64".
	Encoding string
	// Data is the base64 of iv + ciphertext + tag.
	Data string
}

EncryptResult is the output of Cipher.Encrypt: a transport encoding and the encrypted data.

type HistoryParams

type HistoryParams struct {
	// Limit caps how many messages are returned. The server applies its own cap and a
	// default when zero.
	Limit int
	// Before, a message serial (see [Message.Serial]), pages backward: only messages
	// with a serial strictly below it are returned. Zero means start from the newest.
	Before uint64
}

HistoryParams are the query params for Channel.History.

type HistoryResult

type HistoryResult struct {
	// Messages are the matching messages, ordered oldest-first.
	Messages []*Message
	// More is true when older messages remain beyond this page. Pass the oldest
	// message's Serial as [HistoryParams.Before] to fetch them.
	More bool
}

HistoryResult is one page of channel history.

type Message

type Message struct {
	// Channel is the channel the message was published to.
	Channel string
	// Name is the application-level event name the message was published under.
	Name string
	// Data is the message payload as published (decrypted on a channel with a cipher).
	// Unmarshal it into your own type with json.Unmarshal.
	Data json.RawMessage
	// Timestamp is the server publish time in milliseconds since the Unix epoch.
	Timestamp int64
	// ID is the unique message id, for dedup and idempotent publishing. Batch members
	// share their batch's id with a ":<index>" suffix.
	ID string
	// ClientID is the client id of the publisher, when known.
	ClientID string
	// Encoding says how Data is encoded (e.g. "cipher+aes-256-gcm/base64" when a
	// message could not be decrypted). Empty for plain JSON.
	Encoding string
	// Serial is the contiguous per-channel serial (0 for ephemeral/unsequenced
	// messages). The SDK uses it to detect gaps, as the resume cursor, and it is
	// history's Before cursor to page backward from this message.
	Serial uint64
	// Ephemeral marks a fire-and-forget message: not stored in history and not
	// replayed on resume.
	Ephemeral bool
}

Message is one message delivered to subscribers or returned from history.

type Options

type Options struct {
	// Endpoint is the Realtime edge host or an absolute ws(s) URL. Defaults to
	// "realtime.foony.io", which resolves to wss://realtime.foony.io.
	Endpoint string
	// Key is a Realtime API key in "appSlug.publicKeyId:privateKey" form. The key is a
	// long-lived secret, so use it only in server-side code and trusted quick starts.
	// Never ship it in client-side code distributed to users: those should use
	// short-lived JWTs from AuthCallback.
	Key string
	// ClientID is the client id to attach when authenticating with Key. With token
	// auth the client id comes from the JWT's subject instead, and this option is not
	// sent.
	ClientID string
	// Token is a static JWT to send in the auth handshake. Mutually exclusive with
	// AuthCallback. Useful for local dev and short scripts. A static token is never
	// renewed: once it expires, the connection ends in the terminal
	// [ConnectionFailed] state, so use AuthCallback for anything long-running.
	Token string
	// AuthCallback returns a fresh JWT. Called once on connect and again on every
	// reconnect. This is the recommended auth method for anything long-running,
	// because the SDK can renew the token whenever it needs one. See the auth docs:
	// https://foony.io/docs/auth
	AuthCallback func(ctx context.Context) (string, error)
	// DisableAutoReconnect stops the SDK from reconnecting after unexpected
	// disconnects. If false (the default), the SDK reconnects with exponential
	// backoff. If true, a dropped connection stays down until you call Connect again.
	// An auth error that cannot be recovered (a bad or expired static Token, or a bad
	// Key, with no AuthCallback to re-mint a credential) still ends in the terminal
	// [ConnectionFailed] state rather than retrying.
	DisableAutoReconnect bool
	// InitialReconnectDelay is the initial backoff for reconnects. The delay doubles
	// each attempt up to MaxReconnectDelay. Defaults to 1 second when zero.
	InitialReconnectDelay time.Duration
	// MaxReconnectDelay caps the reconnect backoff. Defaults to 30 seconds when zero.
	MaxReconnectDelay time.Duration
	// DisableQueueing rejects publishes made while the connection is establishing or
	// temporarily down. If false (the default), those publishes are queued locally and
	// flushed on (re)connect. If true, publishing while not connected returns an error
	// immediately.
	DisableQueueing bool
	// Batch configures the always-on auto-batching applied to every channel,
	// overridable per channel with [WithBatchOptions]. Defaults are documented on
	// [BatchOptions].
	Batch *BatchOptions
}

Options configures a Client. Exactly one of Key, Token, or AuthCallback must be set, or New returns an error.

type PaginatedResult

type PaginatedResult[T any] struct {
	// Items are the items on this page.
	Items []T
	// contains filtered or unexported fields
}

PaginatedResult is one page of a paginated response. Items is the current page, and Next fetches the following page (older messages for a newest-first history).

func (*PaginatedResult[T]) HasNext

func (p *PaginatedResult[T]) HasNext() bool

HasNext reports whether another page exists.

func (*PaginatedResult[T]) IsLast

func (p *PaginatedResult[T]) IsLast() bool

IsLast reports whether this is the final page.

func (*PaginatedResult[T]) Next

func (p *PaginatedResult[T]) Next(ctx context.Context) (*PaginatedResult[T], error)

Next fetches the next page. It returns nil, nil when this is the last one, and a RestError when the fetch fails.

type Presence

type Presence struct {
	// contains filtered or unexported fields
}

Presence is the presence surface for one channel. Announce this connection with Presence.Enter / Presence.Update / Presence.Leave, and listen on who comes and goes with Presence.On. Reached via Channel.Presence. See https://foony.io/docs/presence for the full model.

channel.Presence.On(realtime.PresenceEnter, func(member *realtime.PresenceEvent) {
	fmt.Println(member.ClientID, "joined")
})
err := channel.Presence.Enter(ctx, map[string]string{"status": "online"})

func (*Presence) Enter

func (p *Presence) Enter(ctx context.Context, data any) error

Enter announces this connection as present on the channel, with optional data (a display name, a status) shown to other members. Pass nil for no data. It implicitly attaches the channel. The membership is remembered, and the SDK re-enters it automatically after a reconnect. It returns nil once the server acks the entry, and the server's error when the token lacks the presence capability.

func (*Presence) Leave

func (p *Presence) Leave(ctx context.Context) error

Leave removes this connection's presence entry and stops the automatic re-entry on reconnect. It returns nil once the server acks the leave, and the request's error when it fails.

func (*Presence) Off

func (p *Presence) Off()

Off removes every presence listener, dropping the server-side watcher.

func (*Presence) On

func (p *Presence) On(action PresenceAction, listener func(*PresenceEvent)) func()

On registers a listener for presence events with a matching action and returns its unsubscribe function. Like Presence.Subscribe, the first listener opens the server-side presence watcher.

func (*Presence) Once

func (p *Presence) Once(action PresenceAction, listener func(*PresenceEvent)) func()

Once registers a listener invoked one time for the next presence event with a matching action, and returns its unsubscribe function.

func (*Presence) Subscribe

func (p *Presence) Subscribe(listener func(*PresenceEvent)) func()

Subscribe registers a listener for every presence event and returns its unsubscribe function. Adding the first listener asks the server for presence on this channel: an initial member snapshot, then live transitions. This is independent of a message Subscribe, so a channel used only for messages never opens a presence watcher, and the watcher is dropped again when the last presence listener is removed.

func (*Presence) Update

func (p *Presence) Update(ctx context.Context, data any) error

Update replaces the data on this connection's presence entry. Other members receive an update event. It returns nil once the server acks the update, and the server's error when the token lacks the presence capability.

type PresenceAction

type PresenceAction string

PresenceAction is a presence transition: a member entered, left, or updated its data.

const (
	// PresenceEnter means a member announced itself present on the channel.
	PresenceEnter PresenceAction = "enter"
	// PresenceLeave means a member's presence entry was removed.
	PresenceLeave PresenceAction = "leave"
	// PresenceUpdate means a member replaced the data on its presence entry.
	PresenceUpdate PresenceAction = "update"
)

The recognized presence transition values.

type PresenceEvent

type PresenceEvent struct {
	// Channel is the channel the presence transition occurred on.
	Channel string
	// Action says which transition occurred (enter, leave, or update).
	Action PresenceAction
	// ClientID is the client id of the member whose presence changed.
	ClientID string
	// ConnectionID is the connection id of the member whose presence changed.
	ConnectionID string
	// Data is the presence payload supplied on enter/update, if any (decrypted on a
	// channel with a cipher).
	Data json.RawMessage
	// Encoding says how Data is encoded when it could not be decrypted. Empty for
	// plain JSON.
	Encoding string
	// Timestamp is the transition time in milliseconds since the Unix epoch.
	Timestamp int64
}

PresenceEvent is one presence transition delivered to presence listeners.

type PresenceMember

type PresenceMember struct {
	// ClientID is the member's clientId.
	ClientID string `json:"clientId"`
	// ConnectionID is the member's connection id (one clientId may hold several).
	ConnectionID string `json:"connectionId"`
	// Action is always "present" in a snapshot.
	Action string `json:"action"`
	// Data is the presence payload (decrypted when the channel has a cipher).
	Data json.RawMessage `json:"data"`
	// Encoding is the remaining payload encoding when the data could not be decoded.
	Encoding string `json:"encoding"`
	// Timestamp is when the member last entered or updated, in ms since the Unix
	// epoch.
	Timestamp int64 `json:"timestamp"`
}

PresenceMember is one current member returned from RestPresence.Get.

type PublishOption

type PublishOption func(*publishSettings)

PublishOption customizes one publish call.

func WithEphemeral

func WithEphemeral() PublishOption

WithEphemeral marks the publish fire-and-forget: delivered live to current subscribers but never stored, so it is excluded from history and reconnect replay. For transient events on a channel that otherwise persists.

type PublishResult

type PublishResult struct {
	// MessageID is the server-assigned (or echoed) message id. A batch publish shares
	// one id.
	MessageID string `json:"messageId"`
	// Serial is the contiguous per-channel serial for durable publishes. Zero for
	// ephemeral ones.
	Serial uint64 `json:"serial"`
}

PublishResult is the result of a successful REST publish.

type Rest

type Rest struct {
	// Auth mints tokens against the service, authenticated by this client's key.
	Auth *RestAuth
	// Channels is the map-like accessor for channels: a stable instance per name.
	Channels *RestChannels
	// contains filtered or unexported fields
}

Rest is the REST client. Construct it with NewRest and use Channels.Get(name) for publish, history, and presence reads. Use the WebSocket Client instead when you need to receive live messages.

// Server-side: an API key is the simplest auth method here.
rest, err := realtime.NewRest(realtime.RestOptions{Key: os.Getenv("REALTIME_API_KEY")})
channel := rest.Channels.Get("chat:room-1")
_, err = channel.Publish(ctx, "greeting", map[string]string{"text": "hi"})
page, err := channel.History(ctx, realtime.RestHistoryParams{Limit: 10})
fmt.Println(page.Items)

func NewRest

func NewRest(options RestOptions) (*Rest, error)

NewRest builds a Rest client. It returns an error unless one of RestOptions.Key, RestOptions.Token, or RestOptions.AuthCallback is set.

func (*Rest) Time

func (r *Rest) Time(ctx context.Context) (int64, error)

Time fetches the current service time, in ms since the Unix epoch. Useful for measuring clock skew. It returns a RestError when the request fails.

type RestAuth

type RestAuth struct {
	// contains filtered or unexported fields
}

RestAuth mints tokens against the service, from a Rest client's Auth field.

func (*RestAuth) RequestToken

func (a *RestAuth) RequestToken(ctx context.Context, params TokenParams) (*TokenDetails, error)

RequestToken asks the service to mint a client JWT from this client's API key. The granted capability must be a subset of the key's own. It returns the TokenDetails, whose Expires lets callers cache the token. It returns an error when this client has no Key, and a RestError when the service refuses, for example a capability outside the key's grant. See https://foony.io/docs/auth for the full token flow.

type RestChannel

type RestChannel struct {
	// Name is the channel name this instance is bound to.
	Name string
	// Presence reads this channel's current members.
	Presence *RestPresence
	// contains filtered or unexported fields
}

RestChannel is a channel handle for REST operations: publish, history, and presence. Obtained from Channels.Get(name). It holds no server-side state.

func (*RestChannel) History

History reads the channel's message history, newest first by default. Batch publishes come back as one item per message, sharing the batch's id and serial. On a channel with a cipher, messages are decrypted before they are returned. How far back history reaches depends on each message's retention, see https://foony.io/docs/history. It returns one page. Page through older messages with the result's Next. It returns a RestError when history cannot be read.

func (*RestChannel) Publish

func (ch *RestChannel) Publish(ctx context.Context, name string, data any) (*PublishResult, error)

Publish publishes one message from an event name plus payload. On a channel with a cipher, the payload is end-to-end encrypted before it is sent. It returns the PublishResult once the service has accepted the message durably, and a RestError when the request fails, for example a key without the publish capability.

func (*RestChannel) PublishMessages

func (ch *RestChannel) PublishMessages(ctx context.Context, messages ...RestPublishMessage) (*PublishResult, error)

PublishMessages publishes one or more RestPublishMessage values, which can also set ClientID, ID, or Ephemeral. Several messages are stored and delivered as one atomic batch under one id.

type RestChannels

type RestChannels struct {
	// contains filtered or unexported fields
}

RestChannels is the channel registry of one Rest client.

func (*RestChannels) Get

func (chans *RestChannels) Get(name string, options ...ChannelOption) *RestChannel

Get returns the RestChannel named name, creating it on first use. Options (e.g. WithCipher) apply when the channel is first created.

func (*RestChannels) Release

func (chans *RestChannels) Release(name string)

Release removes the channel instance for name. A no-op when it doesn't exist.

type RestError

type RestError struct {
	// Code is the machine-readable protocol code (the same table as the Code*
	// constants).
	Code int
	// StatusCode is the HTTP status of the response.
	StatusCode int
	// Message is a human-readable error description.
	Message string
}

RestError is the error for any non-2xx REST response. Check it with errors.As.

func (*RestError) Error

func (e *RestError) Error() string

Error formats the error with its protocol code and HTTP status.

type RestHistoryParams

type RestHistoryParams struct {
	// Limit is the page size. The service default is 100 when zero.
	Limit int
	// Before is an exclusive serial cursor: return only messages with a serial
	// strictly below it. Pass the oldest Serial you already have (see
	// [RestMessage.Serial]) to page backward.
	Before uint64
	// Direction is "backwards" (newest first, the default) or "forwards" (oldest
	// first).
	Direction string
}

RestHistoryParams are the query params for RestChannel.History.

type RestMessage

type RestMessage struct {
	// ID is the message id. Batch members share their batch's id.
	ID string `json:"id"`
	// Name is the application-level event name.
	Name string `json:"name"`
	// Data is the payload (decrypted when the channel has a cipher).
	Data json.RawMessage `json:"data"`
	// Timestamp is the publish time, in ms since the Unix epoch.
	Timestamp int64 `json:"timestamp"`
	// ClientID is the publisher's clientId.
	ClientID string `json:"clientId"`
	// Encoding is the remaining payload encoding, e.g. a cipher tag when no cipher is
	// configured.
	Encoding string `json:"encoding"`
	// Serial is the contiguous per-channel serial (zero for unsequenced messages).
	Serial uint64 `json:"serial"`
}

RestMessage is one message returned from RestChannel.History.

type RestOptions

type RestOptions struct {
	// Endpoint is the service host or an absolute http(s) URL. Defaults to
	// "realtime.foony.io", which resolves to https://realtime.foony.io.
	Endpoint string
	// Key is a Realtime API key in "appSlug.publicKeyId:privateKey" form. This is the
	// preferred (and simplest) auth method for server-side callers. The key is a
	// long-lived secret, so keep it server-side.
	Key string
	// Token is a static JWT, sent as a Bearer token. Mutually exclusive with
	// AuthCallback.
	Token string
	// AuthCallback returns a fresh JWT. Called before the first request and again
	// whenever the service reports the current token expired.
	AuthCallback func(ctx context.Context) (string, error)
	// ClientID is the default clientId stamped on published messages that don't set
	// their own. Only useful with key auth, to attribute a backend's publishes to a
	// user. When omitted, the service attributes each publish to the authenticated
	// identity, so token-auth callers never need to set this: the token's clientId
	// applies automatically, and a differing value is rejected.
	ClientID string
	// HTTPClient overrides the HTTP client. Mostly useful in tests. Defaults to
	// http.DefaultClient.
	HTTPClient *http.Client
}

RestOptions configures a Rest client. One of Key, Token, or AuthCallback is required.

type RestPresence

type RestPresence struct {
	// contains filtered or unexported fields
}

RestPresence reads presence for one channel, from the channel's Presence field.

func (*RestPresence) Get

Get fetches the channel's current members. The snapshot is complete (presence sets are bounded), so the result is a single page. Members' Data is decrypted when the channel has a cipher. It returns a RestError when the request fails.

type RestPresenceParams

type RestPresenceParams struct {
	// Limit caps the number of members returned.
	Limit int
	// ClientID returns only members with this clientId.
	ClientID string
	// ConnectionID returns only the member on this connection.
	ConnectionID string
}

RestPresenceParams are the query params for RestPresence.Get.

type RestPublishMessage

type RestPublishMessage struct {
	// Name is the application-level event name.
	Name string `json:"name"`
	// Data is the JSON-serializable payload.
	Data any `json:"data"`
	// ClientID attributes the message to a clientId. Only useful with key auth, which
	// may name any user. Token auth needs no value here (the token's clientId applies
	// automatically) and anything else is rejected.
	ClientID string `json:"clientId,omitempty"`
	// ID is a stable id reused across resends so the server can drop duplicates of
	// the same publish. Single-message publishes only.
	ID string `json:"id,omitempty"`
	// Ephemeral marks the message fire-and-forget: delivered live but excluded from
	// history and resume.
	Ephemeral bool `json:"ephemeral,omitempty"`
}

RestPublishMessage is one message to publish over REST.

type ServerError

type ServerError struct {
	// Code is the machine-readable error code. See the Code* constants.
	Code int
	// Message is a human-readable error description for logging and debugging.
	Message string
}

ServerError is a protocol or authorization error the service returned for a request. Check the machine-readable ServerError.Code against the Code* constants with errors.As:

var serverErr *realtime.ServerError
if errors.As(err, &serverErr) && serverErr.Code == realtime.CodeCapability {
	// the token does not grant this action
}

func (*ServerError) Error

func (e *ServerError) Error() string

Error formats the error as "server error <code>: <message>", matching the realtime-js SDK's error strings.

type TokenDetails

type TokenDetails struct {
	// Token is the signed JWT to authenticate WebSocket or REST calls with.
	Token string `json:"token"`
	// KeyName is the name of the key that requested it, "appSlug.publicKeyId".
	KeyName string `json:"keyName"`
	// Issued is the issue time, in ms since the Unix epoch.
	Issued int64 `json:"issued"`
	// Expires is the expiry time, in ms since the Unix epoch.
	Expires int64 `json:"expires"`
	// ClientID is the clientId the token authenticates as.
	ClientID string `json:"clientId"`
	// Capability is the granted capability as a JSON string.
	Capability string `json:"capability"`
}

TokenDetails is a service-issued token plus the metadata needed to cache it until expiry.

type TokenParams

type TokenParams struct {
	// ClientID is the clientId the token authenticates as. Required.
	ClientID string
	// TTL is the token lifetime. Defaults to one hour when zero, and the service caps
	// it at 24 hours.
	TTL time.Duration
	// Capability is the capability to grant. It must be a subset of the key's own
	// capability, which is also the default when nil.
	Capability Capability
}

TokenParams are the params for RestAuth.RequestToken.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL