Skip to content

Go SDK

The Go SDK, one client with a field for each product (Cube, Sandbox, Browser, Drive, Database). Standard library only, Go 1.21 and later; context first, errors as values, functional options.

Diperbarui

Halaman ini belum diterjemahkan ke Indonesia: berikut versi bahasa Inggrisnya.

Install

Standard library only; Go 1.21 or later. Every call takes a context.Context first, errors are returned, options are functional options, and streams are io.Reader and io.Writer. The module is github.com/GlacAI/glac-go; its package is glac:

go get github.com/GlacAI/glac-go

The client

One client, with a field for each product:

client, err := glac.NewClient() // GLAC_API_KEY and GLAC_API_URL; or glac.NewClient(glac.APIKey(key), glac.BaseURL(address))
if err != nil {
	log.Fatal(err)
}

app, err := client.Cube.Deploy(ctx, "ghcr.io/acme/api:1.4", glac.Name("api"), glac.Port(8080)) // Cube: waits until it serves
if err != nil {
	log.Fatal(err)
}
fmt.Println(app.URL())

sbx, err := client.Sandboxes.Create(ctx, glac.Image("python:3.12")) // Sandbox
if err != nil {
	log.Fatal(err)
}
res, err := sbx.Run(ctx, "python3 -c 'print(6*7)'")
if err != nil {
	log.Fatal(err)
}
fmt.Print(res.Stdout)

drive, err := client.Drives.Create(ctx, glac.Name("data"), glac.SizeBytes(10<<30)) // Drive
if err != nil {
	log.Fatal(err)
}
fmt.Println(drive.ID)

The one call that needs no client of your own is a sandbox for one command, on the default client (made from the environment the first time it is needed):

res, err := glac.Sandboxes.Run(ctx, "echo hello") // one call: make, run, delete
if err != nil {
	log.Fatal(err)
}
fmt.Print(res.Stdout)

Each product is a field of the client (go-github’s way: a service that makes its calls with that client), and the platform’s own calls are the client’s methods:

Where What Calls
client.Cube Cube: images that run as services, each at an address of its own (the API calls one an app: /v1/apps, the type App). client.Workers is the same service under another name, marked deprecated Deploy, Create, Get, GetOrCreate, List
client.Sandboxes Sandboxes (/v1/sandboxes) Run, Create, Get, GetOrCreate, List, ListPage
client.Sandboxes.Snapshots The sandboxes’ snapshots Get, List
client.Sandboxes.SSHKeys The SSH keys that open sandboxes Create, Get, List
client.Drives Drives: disks of the project’s that outlive their sandboxes, each mounted read-write by one sandbox at a time, and their snapshots, mounted read-only by any number at once (/v1/drives, a control plane’s; the types Drive and DriveSnapshot) Create, Get, List, ListPage, Rename, Delete, Attach, Detach, ForceDetach; Snapshot, Snapshots, GetSnapshot, DeleteSnapshot, Fork, Restore, AttachSnapshot, DetachSnapshot, ForceDetachSnapshot
client.Volumes The volumes underneath Drives (/v1/volumes; the type Volume) Create, Get, List, Backup, Backups
client.Backups The backups of volumes: whole copies of a volume at an instant, in the object store, which outlive it (/v1/backups, a control plane’s; the type Backup) List, ListOf, Get, Delete, Restore
client.Domains The project’s own domains (/v1/domains; a control plane’s; the type Domain) Create, Get, List, Delete
client.Webhooks, client.Keys Where the project’s events are sent (/v1/webhooks), and its API keys (/v1/keys); a control plane’s Create, Get, List, Update (webhooks), Delete
client.Destinations Where the logs, metrics and traces of the project’s apps are exported (/v1/destinations); a control plane’s Create, Get, List, Update, Delete, Test
client.Secrets, client.Variables The project’s secrets and shared variables Put, Update (a secret’s hosts), List, Delete
client.Deploys The project’s deploys: several of its services deployed together (/v1/deploys, a control plane’s; the type Deploy) Create, Get, List
client itself The platform: the account, usage, the bill and the balance, the project and its events GetAccount, UpdateAccount, GetUsage, GetUsageReport, GetBilling, UpdateBilling, GetBalance, UpdateBalance, TopUp, ListCharges, ListLedger, GetProject, ListEvents, Request
glac.Sandboxes The default client’s Sandboxes, with their Snapshots and SSHKeys The same calls as client.Sandboxes, made with the default client; where none can be made of the environment, each returns why

The key is read from GLAC_API_KEY and the address from GLAC_API_URL (default https://api.glac.ai). glac.NewClient(glac.APIKey(…), glac.BaseURL(…), glac.RequestTimeout(d), glac.Retries(n), glac.HTTPClient(…), glac.OnResponse(f)) takes another address, key, timeout or number of retries. An API the SDK does not wrap: client.Request(ctx, "GET", "/v1/webhooks", nil, nil, &out).

Cube

A Cube, which the API calls an app, is an image that runs as a service at an address of its own (the type App here), with as many instances as its requests need and none while there are none. Every deploy makes a version, which takes the app’s traffic once it is ready.

client.Cube.Deploy(ctx, image, …) does what glac deploy does: with Name, the app of that name gets a version of the image, and is made where there is none; without it, a new app is made and the API names it. It waits until the version has the traffic, and returns the app as it is then (app.URL(), app.CurrentVersion()).

app, err = client.Cube.Deploy(ctx, "ghcr.io/acme/api:1.5", glac.Name("api"), glac.MinInstances(1)) // the app is there: MinInstances is changed first, then a version is made
var failed *glac.DeployError
if errors.As(err, &failed) {
	log.Fatalf("version %d did not come up: %s", failed.Version.Number, failed.Code()) // the traffic stayed where it was
} else if err != nil {
	log.Fatal(err)
}
fmt.Println(app.CurrentVersion())
  • To an app that is there, what holds at once (Regions, the scale, the keys, WithEgress, Metadata, the log’s settings, the traces’ settings, Public, DependsOn, Aliases, EndpointMode) is changed first, as app.Update changes it, and only where some of it is given; then a version is made of what a version is made of (Image, Port, Env, Secrets, StartLine, VCPUs, Volumes, Drives, KeyDrives, …), as app.Deploy makes one: what is not given is the app’s, and Env and Secrets are all the version has. An app that is not there (404 app_not_found) is made with all of it, as Create makes one; one that somebody else made in between (409 name_taken) is deployed to as one that is there.
  • The other calls. client.Cube.Create, Get, GetOrCreate, List; and on an app app.Deploy (a version: the *AppVersion), app.Update, app.Version(ctx, "3"), app.Versions, app.Rollback(ctx, "1"), app.Logs(ctx, glac.Follow()), app.LogUsage, app.DeleteLogs, app.Metrics(ctx, from, to, "minute") (what the machines that serve it counted of it, a step a minute or an hour: metrics), glac.TraceSampleRate(0.1) and glac.TrustIncomingTrace(true) of Create, Deploy and app.Update, and app.Info().Traces (a span of the platform’s for a share of the app’s requests, which the instance’s own spans are children of, none unless given, sent to the project’s destinations that take traces: traces), app.Info().LogsState (whether each machine that keeps its log keeps what is printed there now for the long term: logs), app.Instance(ctx, key) (an instance kept for a key), app.CreateKeySecret, app.KeySecrets, app.KeySecret(ctx, id), app.DeleteKeySecret (the secrets an app’s keys are signed with) and glac.SignKey(secret, app.ID, key, expires) (a key signed, as a request through the edge carries it; glac.PathSegment writes it as a path segment: signed keys), app.Delete.
  • Waiting. A call that has a version built waits for it unless given glac.NoWait(). A version that does not come up is a *glac.DeployError, whose Code() is the version’s own (boot_failed, image_not_found, …); the app’s traffic stays where it was, and the app comes back with the error.
  • Runtime settings. glac.Runtime(glac.ServiceRuntime{…}) gives a version its runtime settings: limits of its processes by Docker’s names, the kernel settings Docker lets a container set, file systems in memory, its resolver and the size of /dev/shm, replaced whole.
  • Checkpoints. glac.Checkpoint(n) (every n seconds, 1 to 600; 0 for the default, every 10) has the app’s keyed instances checkpointed while they are awake, and glac.NoCheckpoint() takes that away; both are the app’s own settings, taken by Cube.Create, Cube.Deploy and app.Update, and AppInfo.Checkpoint (an *AppCheckpoint, nil for none) says it. An instance’s SandboxInfo.Checkpoint (a *SandboxCheckpoint: EverySeconds, LastAt, BehindSeconds, BytesPerSecond, StoredBytes, Error; nil without them) says how its checkpoints go.
  • A key’s instance’s version. inst.Upgrade(ctx, "3") (or app.UpgradeInstance(ctx, key, version)) moves the instance of a key to another version of the app, in place: "" is the app’s current version, else its id or its number (one that is ready, an older one too). It keeps its id, its node, its key, its address, its tokens and its own Drives; its disk, its machine and its processes become the new version’s, and its requests are held at the edges while it is switched. It returns the *Upgrade under way (Status preparing, switching, done or failed; FromVersion, ToVersion, Timings, Error, PutBack; Kind (upgrade, or revert), When, KeepDays, Rollback (what a revert of it needs, kept for KeepDays), Reverts, Batch; an upgrade asked through the API to wait for its instance to sleep is waiting until then, and cancelled where it was cancelled (the API’s DELETE, or its batch stopped): one that was cancelled is over, and its wait raises as one that failed does, with the code cancelled), or nil and no error where it is on that version already; up.Wait(ctx) waits for its end, an *Error of type upgrade_failed (its Code upgrade_failed or upgrade_not_here) where the new version did not come up, the instance on the version it was on. inst.Upgrades(ctx), app.InstanceUpgrades(ctx, key) and app.InstanceUpgrade(ctx, key, id) read them; inst.Refresh(ctx) reads the instance again (VersionID). Options after the version: glac.WhenAsleep() has it wait (waiting) until the instance sleeps and begin then, so that none of its requests is held; glac.KeepDays(n) (0 to 365, 7 unless given) is how long what a revert needs is kept once it is done, billed as snapshots are. A version whose services mount other Drives or volumes than the instance’s is no upgrade in place: it ends failed with upgrade_not_here.
  • Cancel and revert. up.Cancel(ctx) (or app.CancelInstanceUpgrade(ctx, key, id)) cancels an upgrade that waits (for its instance to sleep, or for its turn in a batch): cancelled, the instance on its version (one that has begun is 409 upgrade_started). up.Revert(ctx, glac.KeepDays(n)) (or app.RevertInstanceUpgrade(ctx, key, id)) puts the instance back to what the upgrade kept, its disk, its Drives as they were before it (a snapshot of what they hold now made first) and the version it came from: a new *Upgrade of Kind revert, which goes and is waited for as an upgrade is. Refused 409 rollback_gone where what it goes back to is not kept any more.
  • A batch of keys. app.UpgradeInstances(ctx, glac.BatchVersion("4"), glac.FromVersions("3"), glac.OnlyKeys(…), glac.WhenNow(), glac.KeepDays(n), glac.BatchConcurrency(8), glac.MaxFailures(1)) upgrades the app’s keys to one version (the app’s current one unless given), each once its instance sleeps (or as its turn comes with WhenNow), 4 at once unless given (two on one node), stopped failed after 5 failures unless given. It returns the *UpgradeBatch (Status running, done, failed, stopped; StopReason, Counts, Upgrades, Skipped, Origin); batch.Wait(ctx) returns nil however it ended (a member whose instance never sleeps keeps it running: the context’s deadline ends the wait with wait_timeout), batch.Stop(ctx) cancels the members that wait. app.UpgradeBatches(ctx), app.UpgradeBatch(ctx, id) and app.StopUpgradeBatch(ctx, id) read and stop them.
  • A key’s instance’s ports. app.OpenInstancePort(ctx, key, "tcp", 5432, "2001:db8::/32") opens a port at the own public IPv6 address of the instance of a key, wherever it is, to the sources given (IPv6 prefixes or addresses, ::/0 for everybody), and returns the OpenedPort, with where it is reached; app.InstancePorts(ctx, key, "tcp") lists them, and app.CloseInstancePort(ctx, key, "tcp", 5432) closes one, saying whether it was open. "udp" for UDP. They are the ports of its sandbox (keyed instances); an instance with no address of its own is refused (409 no_ipv6).
  • Several together. client.Deploys.Create(ctx, map[string][]glac.DeployOption{"api": {glac.Image("ghcr.io/acme/api:2")}, "web": nil}) deploys several services of the project at once (a project’s deploy): each is given what App.Deploy takes (nil: as it is), they are taken in the order of what each waits for and switched together, and it waits for the deploy (glac.NoWait() does not), following a deploy that took its place. One that fails is a *glac.DeployError with Deploy and Service, whose Code() is the deploy’s (job_failed, job_timed_out, job_lost, …); it unwraps to an *Error of type deploy_failed whose Param is services.<name>. client.Deploys.Get and List read them.
  • A control plane’s. Against a node that stands alone, every call of client.Cube is that node’s 404 no_such_route.

Sandbox

sbx, err := client.Sandboxes.Create(ctx) // stays until deleted
if err != nil {
	log.Fatal(err)
}
defer sbx.Delete(ctx)
res, err := sbx.Run(ctx, "python3 -c 'print(6*7)'") // res.Stdout, res.Stderr, res.ExitCode
if err != nil {
	log.Fatal(err)
}
link, err := sbx.URL(3000)
if err != nil {
	log.Fatal(err)
}
fmt.Println(res.Stdout, link)

A command given as one string runs with sh -c; with glac.Args(...) it is a program and its arguments, run as they are. glac.Stdout(os.Stdout) streams the output while Run waits; sbx.Exec returns at once, and cmd.Output(ctx) reads its output as it comes (it is also an io.Reader). client.Sandboxes.Run is a sandbox for one command; sbx.Run runs one in a sandbox.

Call What it does
client.Sandboxes.Run(ctx, cmd, …) A sandbox made for one command, run and deleted; the options of Create and of a sandbox’s Run.
client.Sandboxes.Create(ctx, Image(…), Name(…), Env(…), Metadata(…), Volumes(…), Drives(…), PublicPorts(…), SleepAfter(d), NeverSleep(), SleepNotice(d), SleepHoldMax(d), ExpiresAt(t), WithEgress(…), FromSnapshot(…)) Makes a sandbox; every option may be left out. A service started when it boots: AutoStart(true) (the image’s own start command), StartLine(…) or StartArgv(…), with ReadyTCPOn(port), ReadyHTTPOn(port, path) or ReadyCommand(…); Hostname(…), StopSignal(…) and StopGrace(d); ProjectNetwork() puts it on the project’s private network from the start (Info().ProjectAddress); VolumeMount{…, Copy: true} gives a new volume what the image has at its path (making a sandbox).
sbx.Info().AppID, VersionID, Key, ImageDigest A sandbox made through the control plane is an instance of an app: the project’s default sandbox app (sbx-sandbox) when it was made with no image, else the app of its image (the app’s Instances lists that app’s sandboxes, each under its key); the version of that app it is of, the image it was made of (a tag that was moved since is a new version of the app, which the sandboxes made after it are of); its key there, its name or its id; and its image by digest. Said by its create’s answer and by every read of it, and kept; empty for one made from a template or a snapshot, and for a fork.
Init("systemd") of Create and Run; SandboxInfo.Init A whole system: the image’s own systemd is the sandbox’s PID 1 (without Image, the platform’s Ubuntu with systemd and OpenSSH), so packages, services started with systemctl, cron and the journal work as on a machine, and it has a machine ID of its own, kept through sleeps, wakes and moves. Not with a start command or a ready check (400 invalid_start), nor with FromSnapshot. SandboxInfo.Init is "systemd" for one and empty for the platform’s init.
client.Sandboxes.Get, GetOrCreate, List(ctx, Status(…), MetadataIs(k, v)), ListPage(ctx, PageSize(n), After(cursor)) Finds; finds or makes; lists; one page.
SuspendAfter(d), WakeDailyAt("03:30") of Create and sbx.Update (DefaultSuspendAfter(), NoDailyWake() take them back); SandboxInfo.SuspendAfterMS, WakeDailyAt How long it stays paused, once idle has stopped it, before its memory is saved and it sleeps for real (a second to a day; its node holds it to ten minutes at the most), and a time of day in UTC at which it is woken every day while it rests, for work of its own that waits for a wake. Given to an app, its keyed and stateful instances’.
OnExpiry("sleep"), HeldAsleep(true) of Create and sbx.Update; SandboxInfo.OnExpiry, HeldAsleep What ExpiresAt does when it comes: delete the sandbox (the default), sleep (put it to sleep and keep it, a request waking it as ever) or hold (the same, held asleep); and whether a request to one of its ports wakes it while it sleeps: held asleep, its preview address answers 502 sandbox_paused, and a call of the API wakes it.
sbx.Run(ctx, cmd, Args(…), Dir(…), Env(…), User(…), Timeout(d), Stdin(r), Stdout(w), Stderr(w)) Runs and waits. *Result has Stdout, Stderr, ExitCode, Duration, Usage, Wake.
sbx.Exec(ctx, cmd, StdinOpen(), TTY(cols, rows)), sbx.Terminal(ctx, …) Starts a command; a terminal (an io.ReadWriteCloser).
sbx.Files Read, Open, Write, WriteFrom, List, Stat, Exists, Mkdir, Move, Remove, and SetAttributes(ctx, path, glac.FileAttributes{Mode, SizeBytes, ModifiedAt}): a file’s mode, its length, its time of last modification.
sbx.URL(port), sbx.Token(ctx, Scopes(…), ExpiresIn(d)) A link to a port (signed locally); a token for this sandbox.
sbx.Info().IPv6, sbx.OpenPort(ctx, "tcp", port, from…), sbx.Ports(ctx, "tcp"), sbx.ClosePort(ctx, "tcp", port), Inbound(InboundPort{…}) Its own public IPv6 address, where it has one (empty otherwise); a port opened at it to the sources listed (IPv6 prefixes or addresses, "::/0" for everybody), with where it is reached; the ports open; one closed (it says whether it was open); the whole list, given to Create or Update. Inbound is closed by default; what is open stays open while it sleeps and when it moves. See networking.
sbx.RevokeToken(ctx, token) That token alone is refused from then on, and every other works as before: the token itself (Token.Token) or its id (Token.ID, tok_…). A sandbox keeps 1 000 such revocations that have not run out (409 too_many_revoked_tokens past them: RevokeTokens then). Returns a *TokenRevocation with its TokenID and ExpiresAt.
sbx.RevokeTokens(ctx) Every token handed out for this sandbox so far stops working (401 invalid_token), those in preview links and cookies and those SSH sessions hold too; tokens made after it work. Needs the API key. Returns a *TokenRevocation: SandboxID, Generation, RevokedAt.
sbx.Update, sbx.Sleep, sbx.Restart, sbx.Delete, sbx.Snapshot, sbx.Fork(ctx, n), sbx.Restore Change it; sleep; boot from disk; delete; snapshot; fork; restore.
glac.Checkpoint(n) to Create or Update, glac.NoCheckpoint() to Update; Info().Checkpoint Checkpoints while it is awake (1 to 600 seconds apart; 0: ten), so that it goes on with its processes when its machine is lost, and none again; how they go. 409 checkpoints_unavailable where no machine takes them.
Info().DiskWrites What its disk has written today (a UTC day), against the day’s share at full speed (a *SandboxDiskWrites): TodayBytes, of which CheckpointCopyBytes its checkpoints’ copies wrote to the host’s disk; FullSpeedUntilBytes, the share (100 GiB unless its node is told otherwise; nil: its node slows nothing, and counts all the same); Limited and LimitedUntil: past the share its disk takes writes at 2 MiB a second until the day ends, and no write fails. Nil on a node that does not copy its disks to an object store, which counts nothing. What it writes to its Drives is not counted here: writing to a Drive costs nothing.
client.Sandboxes.Snapshots.Get, client.Sandboxes.Snapshots.List(ctx, OfSandbox(id)), snap.Rename(ctx, name), snap.ExpireAt(ctx, t) Snapshots: a sandbox as it was at one instant, memory and processes included; another name for one ("": none), or when it deletes itself (the zero time: never).
client.Sandboxes.SSHKeys.Create(ctx, publicKey, OfSandbox(…), ExpiresAt(t)), Get, List SSH keys: whoever holds the private half gets ssh <sandbox>@<gateway>. A private key is refused before anything is sent.

Drive

A Drive is a disk of the project’s that outlives its sandboxes: mounted read-write by one sandbox at a time, when the sandbox is made or while it runs, and kept, with what it holds, when the sandbox goes, for the next sandbox to mount: of its region, or of another whose machines read its home’s object store (below). It is copied into its region’s object store as it is written, so a sandbox on another node mounts it too: it is brought there first. Only what is written is billed (UsedBytes). Its snapshots are versions of it in that store, and nothing is copied for them: a fork is a new Drive made of one, a restore takes the Drive back to one, and any number of sandboxes mount one read-only at once (below). Drives are a control plane’s (/v1/drives): against a node that stands alone every call returns the node’s 404 no_such_route, and its disks are volumes (below).

  • client.Drives.Create(ctx, glac.Name("data"), glac.SizeBytes(10<<30), glac.InRegion("eu")) makes one, empty but for its file system; every option may be left out (SizeBytes: a whole number of 4 KiB blocks, at least 16 MiB; 64 GiB unless given, or 5 GiB on Free; Durability("strict"): a guest’s fsync returns once the object store has every write before it). Get, List, ListPage, Rename(ctx, drive, name) (an empty name: none), Delete(ctx, drive).
  • client.Drives.Attach(ctx, sandbox, glac.DriveMount{Path: "/data", Drive: drive.ID}) mounts it in a sandbox that exists, read-write, at once (a sandbox that sleeps is woken); Detach(ctx, sandbox, drive) unmounts it, ForceDetach while a process uses it. Each returns the *Drive, whose Info() is a DriveInfo (ID, a vol-…: a Drive is a volume; Name, SizeBytes, UsedBytes, AttachedTo, Region, Home, Status, Lease: the node of its one writer, the epoch, until when; AppID and Key of a keyed instance’s; ParentDriveID and RootDriveID of a fork: the Drive it was forked from, which may be deleted since, and the first of its line, empty for a Drive that is no fork; ManagedBy of a database’s Drive, nil for one that is none’s) and which has Attach, Detach, ForceDetach, Rename, Refresh, Delete, Snapshot, Snapshots, Fork and Restore of its own.
  • A database’s Drive (Info().ManagedBy, the database’s ID) is mounted read-write by the database’s instance alone, and renamed, restored and deleted through the database: those calls here, and a read-write mount of it, are refused (409 managed_by_database). Its snapshots and forks are the project’s to take, and a snapshot of it anybody’s to mount read-only. An app a database is made of says so the same way (app.Info().ManagedBy), and its Update, Deploy, Rollback and Delete are refused alike.
  • A new sandbox mounts Drives with glac.Drives(glac.DriveMount{…}) given to client.Sandboxes.Create (not to the one-off Run); an app’s one instance with the same given to a deploy (an app of many instances or of keys is refused, 409 drive_single_writer; a version mounts a Drive read-write, and read-only snapshot mounts are a sandbox’s: a mount with a Snapshot is refused there, 400 unknown_field), and each key’s instance of a keyed app Drives of its own with glac.KeyDrives(glac.KeyDrive{Name: "home", Path: "/home/app"}).
  • A Drive is one sandbox’s at a time: one that another sandbox has is refused (409 drive_attached), and so is deleting one that is attached. A keyed instance’s Drive goes with its key (409 key_drive).
  • One that another node holds is brought to the sandbox’s node first; while the object store does not hold it as it is yet (503 copy_behind), or it could not be brought (503 drive_not_brought), the call is Retryable: the client tries again as its Retries say, then returns the error, for you to ask again a moment later.
  • Across regions (slice D9). A Drive’s home (home) is the region whose object store holds its copy: the region it was made in. A sandbox of another region mounts it too, where that region’s machines read its home’s store (409 not_on_one_machine where they do not): the Drive is let go as a version and taken over there, read from its home’s store as its guest reads it, and what its guest writes goes to the store of the region it is in now (region). Its home follows it once it has been there for an hour, or at once with client.Drives.MoveHome(ctx, drive) (drive.MoveHome(ctx)): its machine copies what is left in its home’s store into its own, and home is the new region once nothing is left there; DriveInfo.HomeMove says how far the move is (asked_at, asked_by, copied_objects, copied_bytes, objects_left, error). Its old home’s copy goes once no version kept needs it (a day after). A Drive’s home moves once in six hours at the most (409 home_moved_recently); one whose home is the region it is in is 409 home_here.
  • The plan’s limits: 3 Drives of at most 10 GiB, 15 GiB together, on Free; 500 of at most 1 TiB on Paid (403 drive_limit_reached).
  • glac.OfDrive(id), glac.OfKind("drive") and glac.GroupBy("drive") report what Drives kept, were written and read cold. The events drive.created, drive.deleted, drive.attached and drive.detached decode into an EventDrive or an EventDriveAttached.
  • Snapshots. client.Drives.Snapshot(ctx, drive, glac.Name(…), glac.FSConsistent()) (or drive.Snapshot(ctx, …)) takes a snapshot of a Drive, a *DriveSnapshot: a version of it in the object store, kept; nothing is copied. A Drive a running sandbox has read-write is taken with its guest’s writes held for a moment, ten seconds at the most (past them, 503 unavailable, Retryable): glac.FSConsistent() has the sandbox’s agent freeze its file system (Consistency frozen; 409 not_frozen where the agent cannot: take it without), and without it the machine is paused (paused, as at a power cut); one no running sandbox has is at_rest. A Drive whose node copies nothing to an object store has none (409 not_copied). Snapshots(ctx, drive) lists a Drive’s, newest first, and client.Drives.AllSnapshots(ctx, "") the project’s of every Drive ("true": those of Drives deleted since alone); GetSnapshot(ctx, drive, snapshot) finds one (404 snapshot_not_found) and DeleteSnapshot(ctx, drive, snapshot) deletes one. A *DriveSnapshot has Refresh, Delete, Fork, Restore, Attach, Detach and ForceDetach; its Info() is a DriveSnapshotInfo (ID, a dsnap-…; DriveID, Name, Consistency, UsedBytes: what the Drive held then; ExclusiveBytes: what it keeps alone; ExclusiveAt, Version, CreatedAt).
  • A snapshot outlives its Drive: it is listed, read, forked and mounted read-only after the Drive is deleted, by the Drive’s id. It is billed for what it keeps alone (ExclusiveBytes), by the hour, at the Drives’ data price ($0.02 a GB-month).
  • Forks. client.Drives.Fork(ctx, drive, glac.Name(…), glac.FromSnapshot(snapshot)) (or drive.Fork, or snap.Fork(ctx, glac.Name(…))) makes a new Drive whose first version is the snapshot’s, or the Drive’s as it is now; nothing is copied, and it is mounted at once. It returns the new Drive, whose ParentDriveID and RootDriveID say what it was made of, held to the plan’s Drives as a create is (403 drive_limit_reached). A fork is billed by its UsedBytes, its full logical contents, like any Drive: a fork of a 10 GB snapshot is billed as a 10 GB Drive.
  • Restores. client.Drives.Restore(ctx, drive, snapshot) (or drive.Restore(ctx, snapshot), or snap.Restore(ctx)) writes a new version after the Drive’s newest whose content is the snapshot’s; the versions in between stay where a snapshot or a fork keeps them, so restoring to an older snapshot and then to a newer one works. A Drive a sandbox has read-write is refused (409 drive_attached): detach it first.
  • A window of versions. glac.KeepHistory(d) on a create, client.Drives.SetHistory(ctx, drive, d) (0 turns it off), Versions(ctx, drive) and VersionAt(ctx, drive, t) (DriveWindowVersions), glac.ForkAt(t), client.Drives.RestoreTo(ctx, drive, t) (and drive.SetHistory, drive.Versions, drive.RestoreTo; DriveInfo.HistorySeconds, History): a Drive’s window of versions (slice DB8): how far back it can be restored, or forked, by a time, 3600 to 86400 seconds (none unless given; turned off, what it kept goes). Every 30 seconds in which it was written the window gets a version, the Drive at one moment; what they keep alone is billed as a snapshot’s is. A restore or a fork to a time takes the newest version at or before it, which the versions call with that time names.
  • A cache on its node’s NVMe. glac.CacheBytes(n) on a create, client.Drives.SetCache(ctx, drive, n) and drive.SetCache(ctx, n) (glac.CacheAll for all of it), drive.CacheBytes() and DriveInfo.CachedBytes: how much of it its node keeps on its NVMe at the most: all of it unless given (its size); less, down to 0, and what is past it is in the object store alone, read from there when its guest reads it (the least recently read let go first, and never what the store does not hold yet). The cache is billed by the hour as it is filled, beside what the Drive holds. A smaller one lets go of what is past it once the store holds it (of a Drive a running sandbox mounted with all of it cached, from that sandbox’s next sleep or stop); a larger one keeps what its guest reads from then on. The API calls Drives volumes (/v1/volumes); the clients reach them by /v1/drives, which answers as it did. CacheBytes() is its size where the control plane says nothing of a cache; CachedBytes is what of it is on its node’s NVMe now (nil where no node holds it). UsageGroup and UsageReportTotals have CacheGBHours.
  • Read-only mounts. glac.DriveMount{Path: "/snap", Drive: drive.ID, Snapshot: snap.ID} (which sends mode: "snapshot") mounts a snapshot read-only, given to glac.Drives(…) when a sandbox is made, or to client.Drives.AttachSnapshot(ctx, sandbox, mount) (snap.Attach(ctx, sandbox, path)) while it runs; DetachSnapshot(ctx, sandbox, snapshot) and ForceDetachSnapshot take it away by the snapshot’s id. Each answers the snapshot. Any number of sandboxes mount one at once, on any machine of the Drive’s region, whatever has the Drive and after it is deleted. Its blocks come from the object store as the guest reads them: those cold reads cost nothing, and show under that sandbox in usage (DriveColdReadBytes of client.GetUsageReport(ctx, glac.OfSandbox(id))); reads of what that sandbox’s machine already holds cost nothing. The guest cannot write it, and a sandbox that has one does not move to another machine. Attach takes no mount with a Snapshot, AttachSnapshot none without, and DetachSnapshot no id but a dsnap-…: each is refused before anything is sent.

client.Volumes is the API’s /v1/volumes, the disks underneath Drives, kept for the transition: what only it has is a node that stands alone, whose disks are volumes, and shared volumes, file systems several sandboxes mount at once (glac.Shared()). A control plane makes no plain volume for a project’s key. client.Volumes.Create(ctx, glac.Name("team"), glac.Shared()), Get, List, and vol.Delete(ctx) (one that a sandbox still has stays, and that is an error); a volume is mounted with glac.Volumes(glac.VolumeMount{Path: "/team", Volume: vol.ID}), given to client.Sandboxes.Create or sbx.Update, or to an app, whose instances mount it.

Backups of volumes. A backup is a copy of a volume (a Drive is one) at one instant, in the object store, kept until it expires: taken by a call, every day by the volume’s rule, or before a service’s new version replaces its instance. Unlike a snapshot of a Drive it is a copy of its own, a volume is made from it alone, and it outlives its volume; a volume’s backups keep only what changed since the one before, and a block several of them need is billed once, on the earliest. It is making until the store has all of it, then ready, and billed from then by what the store holds of it (StoredBytes) at the price of what sleeping sandboxes keep (backups). Backups are a control plane’s.

  • client.Volumes.Backup(ctx, "data", glac.ExpiresInDays(7)) (or vol.Backup(ctx)) takes one now, a *Backup, kept so many days (1 to 365) or as the volume’s rule says (30 days unless it was set); client.Volumes.Backups(ctx, "data") lists a volume’s, newest first (a deleted volume’s by its id). A node that copies nothing to an object store has none (409 not_copied).
  • client.Backups.List(ctx) lists the project’s, newest first, those of deleted volumes among them, and ListOf(ctx, volumeID) one volume’s; Get(ctx, id), Delete(ctx, id) (nothing more is billed for it; a volume made from it stays), and Restore(ctx, id, glac.Name("data-back")), a new volume made from a ready one, the same as client.Volumes.Create(ctx, glac.FromBackup(id)) (409 backup_not_ready while it is being made, 409 backup_unusable once it failed). A *Backup has the fields of a BackupInfo (ID, a bak-…; VolumeID, and VolumeName, the volume’s name then; Status: making, ready, failed; Reason: manual, daily, before_version; Consistency: frozen, crash; FrozenMS, how long the volume’s writers waited for the copy; SizeBytes, what the volume held, and VolumeSizeBytes, the most it held: what a volume made from it holds; StoredBytes, what it is billed for; OwnBytes, what deleting it would save; SharedBytes; Shared, Region, Failure, CreatedAt, ReadyAt, from when it is billed, and ExpiresAt) and Refresh, Restore and Delete of its own.
  • A volume made from a backup has the size and kind of the volume it was taken of (glac.SizeBytes and glac.Shared are not given with glac.FromBackup), in the backup’s region. Its Status is restoring until it holds the backup whole, then ready, and nothing attaches it before; FromBackup names the backup. Where the machine it was placed on cannot bring it back, the control plane makes it once more on another machine of the region: RestoreAttempt is 2 then, and FirstFailure says why the first try failed. If that fails too, it is failed (Failure), and can only be deleted.
  • glac.OfBackup(id), glac.OfKind("backup") and glac.GroupBy("backup") report what backups kept, on the meter of state (state_gb_hours); Meters.State.BackupsGBHours of client.GetBilling(ctx) says how much of that meter was backups.

Domains

A domain of the project’s is a name that leads to an app, as its default address does, or to a port of a sandbox. Domains are a control plane’s.

Call What it does
client.Domains.Create(ctx, domain, glac.ToApp(…)) or glac.ToSandbox(id, port), with glac.Proxied(…); Get, List(ctx, glac.OfApp(…)), Delete; d.Update(ctx, …), d.Refresh, d.Delete A name for an app or a sandbox’s port, pending_verification until DNS shows a record of Info().Verification.Records (your own domain); 409 domain_exists when the project has it.

Browser

Not open yet. Browser is not open to accounts yet: what follows is the SDK as it stands, ahead of its opening.

A session is a hosted Chromium that a client of the Chrome DevTools Protocol (CDP) drives from outside, at its CDPURL: the address Playwright’s connectOverCDP and Puppeteer’s browserURL take. It is awake while a client is connected and falls asleep, its pages kept, a few seconds after the last one leaves. The addresses carry their credential, so a session prints without them.

session, err := client.Browsers.Create(ctx, glac.BrowserName("agent-42"))
if err != nil {
	return err
}
defer client.Browsers.Delete(ctx, session.ID)
fmt.Println(session.CDPURL) // where a CDP client connects; the credential is in it
  • client.Browsers.Create(ctx, opts...) makes one, awake: glac.BrowserKind("chromium") (on a screen; chromium-headless is the default; glac.BrowserKind("lightpanda"): lightpanda, a browser without a layout engine), glac.BrowserName, glac.BrowserRegion, glac.BrowserSleepAfter, glac.BrowserConnectionIdle, glac.BrowserDeleteAfterIdle, glac.BrowserMetadata, glac.BrowserProfile(name, save) and glac.BrowserProfileVersion(name, version, save) (a profile of the project’s loaded before the session is answered, saved again when it is deleted where save), glac.BrowserProxy(url) (your own HTTP proxy, on Paid; below). The name of one there is answers that one, woken, with its pages. One whose sandbox fails before it runs is no session: its create is refused, 503 session_failed.
  • client.Browsers.Get(ctx, idOrName), Delete(ctx, idOrName) (one made with a profile to save saves it first) and DeleteWithoutSavingProfile(ctx, idOrName). A *BrowserSession has ID, Kind, Status, CDPURL (Playwright’s and Puppeteer’s address), LiveURL, Profile (the one it was made with or saved last: Name, Save, Version), Proxy (its own proxy’s URL and Username, never the password), its times and Usage: AwakeSeconds, and ProxyBytes, what went through its proxy both ways (ProxySentBytes, ProxyReceivedBytes).
  • Your own proxy (glac.BrowserProxy("http://user:password@proxy.example.com:3128"), an HTTP proxy that takes CONNECT): the session’s connections to ports 80 and 443 go out through it, by a relay beside the browser, which is told nothing of it; the credentials stay with the platform. 403 proxy_not_on_plan on Free. See your own proxy.
  • session.Status is awake, asleep, unavailable (its node cannot be reached) or failed (its sandbox failed after its create was answered: its browser stopped and did not come back, say), and then session.Error says why (a *BrowserError: Code, Message, as its node says it; nil unless it failed). The event browser.failed says so too: its data decodes into EventBrowserFailed (BrowserID, Name, Kind, Error.Code).

A session is billed by the Cube resources it uses, as any instance is: its CPU while it computes, its memory while it is awake and its traffic out (pricing and plans). session.Usage.AwakeSeconds is how long it has been awake.

Listing, changing, putting to sleep and new addresses are the Python and TypeScript SDKs’ and glac browsers’. Sessions are a control plane’s.

Databases

A database is a managed Postgres: a PostgreSQL of its own on a Drive, asleep when nothing asks anything of it and woken by a connection, its sessions kept. The owner’s password is in the answer of Create and of ResetPassword alone, and kept nowhere: ConnectionString() of the *Database one of those returned has it.

db, err := client.Databases.Create(ctx, "shop", glac.DatabaseVersion("18"))
if err != nil {
	return err
}
conn, err := pgx.Connect(ctx, db.ConnectionString()) // from a sandbox or a service of the project
  • client.Databases.Create(ctx, name, opts...) makes one and waits for its first start (Status ready, or creating: Get it until it is ready): glac.DatabaseVersion, glac.DatabaseRegion, glac.DatabaseInCluster (the database’s name in the cluster, glacdb), glac.DatabaseSizeOf(vcpus, memoryBytes), glac.DatabaseStorageOf(sizeBytes, durability) (strict, the default, or standard), glac.DatabaseSleepAfter, glac.DatabaseSuspendAfter (seconds paused, a connection answered in a millisecond or two, before its memory is saved and it sleeps for real: 1 to 86400, 300 by default), glac.DatabaseWakeDailyAt("03:30") (woken every day then while it rests; its pg_cron jobs wake it at their own times anyway), glac.DatabaseMetadata. The name of one there is answers that one, without its password. client.Databases.SetSuspendAfter(ctx, idOrName, n) and SetWakeDailyAt(ctx, idOrName, "04:00") change these two, every branch’s at once; 0 and "" take them back (to 300, and to none). On a Free plan a database’s rest is fixed, as Neon’s Free scales to zero: paused a second after its last query (sleep_after_seconds 1) and asleep five minutes later (suspend_after_seconds 300); another value is refused 400 invalid_field, on a create and on a change.
  • client.Databases.CreateBranch(ctx, idOrName, name, glac.BranchParent("main"), glac.BranchExpiresAt(t)) makes a branch of its own: its parent (main unless given) as it is now, nothing copied, with a PostgreSQL, an endpoint and a Drive of its own, at <database>-<name> on the project’s network, deleted by itself at its expiry where one is given. Its roles and passwords are its parent’s: branch.ConnectionString(ownerPassword). DeleteBranch(ctx, idOrName, branch) stops its PostgreSQL and deletes its Drive; not main (409 root_branch), nor a branch others were made of (409 has_branches).
  • glac.DatabaseHistorySeconds(n) on a create, client.Databases.SetHistory(ctx, idOrName, n), client.Databases.Restore(ctx, idOrName, t, glac.Exactly()) and glac.BranchAt(t), glac.BranchAtExactly(t) (Database.HistorySeconds, History): its history (slices DB8 and DB5): how far back its root branch can be restored, or branched from, by a time: a day unless given; 0 (none) to 30 days on Paid, a day on Free. A restore takes the root branch back in place: to a whole minute or a version’s own moment, the newest version of its Drive’s window at or before it (one every 30 seconds in which it was written, a day of it at the most); further back, to the second, or with glac.Exactly(), the moment itself, to the millisecond, by its WAL archive (its WAL replayed to it: seconds to minutes, the call waits). Its endpoint, its connection strings and its owner’s password stay what they were, its connections drop, and its state before is kept as a branch of its own, main_old_<the restore's moment>; one whose new version does not serve within five minutes is undone (503 database_restore_failed). A branch of a time is made of that moment instead (from: {at, exact}). A time its history does not have is 400 invalid_history, its message saying from when the window and the archive reach. A time to the second (or exact) is the archive’s or refused, never the window’s version before it: 400 invalid_history where the archive does not reach it (a database that keeps none, or a time before its oldest base), 503 database_archive_unavailable (retryable) while the archive’s store refuses its segments; each refusal names the window’s version at or before the time, whose own moment can be asked for instead. One whose blocks the store does not give is refused 503 drive_unreadable (retryable), nothing made. An outage of the object store long enough that more of its WAL waited than its Drive keeps for the log gives that WAL up, so that the database stays up: a gap of its archive, in History.Gaps (DatabaseHistoryGap: From, To, FromLSN, ToLSN): when the WAL given up began, until when the archive restores no time (the end of the first base taken after it; null until there is one), and where in the log it begins and ends. A time in a gap is restored only from its Drive’s window (a whole minute, or a version’s own moment); to the second, or exact, it is 400 invalid_history. The gap is said as events too: database.history_gap when it is first found (once a gap, while the database is awake), and database.history_resumed when the archive restores again after it; event.Decode(&gap) reads their data into an EventDatabaseGap: the database, the gap’s From, To (nil of the first), FromLSN and ToLSN, Segments (how many segments of 16 MB were given up) and, of the first, Error (what the object store’s last refusal said).
  • glac.DatabaseTiered() on a create and client.Databases.SetTiering(ctx, idOrName, on) turn its tiered storage on or off (slice DB18: on Paid; each branch’s PostgreSQL restarts once for it); Database.Tiering says whether it is on and what it bills by now (TieredBytes).
  • Get(ctx, idOrName), List(ctx[, name]), Delete(ctx, idOrName, finalSnapshot) (a snapshot of its Drive is kept seven days unless finalSnapshot is false), Branches(ctx, idOrName) (main first, then its own), Branch(ctx, idOrName, "main"), Sleep and Wake (a branch, now), ResetPassword(ctx, idOrName, role) (a new password in this answer alone; "" is the database’s owner).
  • client.Databases.Roles(ctx, idOrName) lists the roles of its root branch that log in, read from PostgreSQL itself ([]DatabaseRole: Name, Owner): its owner and the roles you made (CREATE ROLE … LOGIN); the platform’s are not among them. ResetRolePassword(ctx, idOrName, role) gives any of them a new password, in this answer alone: a *DatabaseRoleSecret with the role’s Password and the root branch’s connection strings of that role (ConnectionString()); the owner’s is made of the database the API answers. A branch of its own keeps the password it was made with.
  • db.ConnectionString() and branch.ConnectionString(password): what libpq, pgx and database/sql’s drivers take, the public one where the API gives one, else the one on the project’s network, with the password (percent-encoded). db.DefaultBranch has ConnectionURI, PooledURI, PrivateURI, Endpoint and State.

A database is reached on the project’s network, from its sandboxes (made with project_network) and its services, at its name: that is its private_uri. Where an edge serves PostgreSQL it is reached from anywhere at its public address too (connection_uri, port 5432, TLS alone; the pooler’s: pooled_uri); they are nil where none does, or where the database has no region to be named under, and the connection string takes them where they are given. A database and a branch print without the password. Databases are a control plane’s.

Managed Applications

An application of the catalogue (Rivet, say) is installed in the project as its own Cube resources: its apps, Volumes, secrets and buckets, labelled with the installation, changed through it alone, and billed as they are (Managed Applications). An install’s answer has every output, the secret ones too (an admin token, an endpoint with it in), and a read shows those to a key with the admin scope alone: an installation object keeps each output an earlier answer gave that a later one hides.

rivet, err := client.Catalogue.Get(ctx, "rivet", "")        // its parameters, and a month of it in Cube's meters
engine, err := client.Installations.Create(ctx, "rivet", glac.InstallationCreate{Parameters: map[string]any{"disk_gib": 20}})
fmt.Println(rivet.Calculator.Month, engine.Info().Outputs["endpoint"])   // RIVET_ENDPOINT, its token in it
Call What it does
client.Catalogue.List(ctx), client.Catalogue.Get(ctx, app, version) The catalogue’s applications, each a CatalogueApp (App, Version, Title, Summary, Verified, Licence, Versions, Parameters: a JSON Schema, Outputs, Calculator with its Month); one, at its newest version or the one named ("": the newest), with its Definition.
client.Installations.Create(ctx, app, glac.InstallationCreate{Name, Version, Region, Parameters, Requires, NoWait}) Installs it, and waits until it is ready (NoWait: at once). One that failed is an *InstallationError (with the Installation), which unwraps to an *Error of the code installation_failed; a context whose deadline passed first, wait_timeout.
client.Installations.List(ctx, app), client.Installations.Get(ctx, idOrName) The project’s installations, the newest first (app "": all); one.
inst.Update(ctx, glac.InstallationUpdate{Version, Parameters, NoWait}), inst.Delete(ctx), inst.Refresh(ctx), inst.Wait(ctx) Another version, or its parameters as a merge patch (a nil value takes one back to its default); deleted with everything it is made of (Status(): deleted, or deleting).
inst.ID(), Name(), Status(), Info() As the API says them: Info() has its Outputs, SecretOutputs, Parameters, Apps, Volumes, Buckets, Requires, Failure.

Managed Applications are a control plane’s.

Secrets and shared variables

  • client.Secrets.Put(ctx, "DB_URL", value, glac.AllowedHosts("db.example.com")) keeps a value the API never gives back, and the hosts it may be sent to; client.Secrets.Update(ctx, name, hosts...) changes the hosts alone; List, Delete. A call that would send it to another host is refused, 403 secret_host_not_allowed.
  • client.Variables.Put(ctx, "REGION", "eu"), List, Delete: the project’s shared variables, which an app’s Env refers to as "${{ shared.REGION }}".

The project, the account and the bill

The platform’s own calls are the client’s methods: client.GetProject and client.ListEvents(ctx, OfType(…)); client.GetAccount and client.UpdateAccount(ctx, EgressOverageCap(n)); client.GetUsage and client.GetUsageReport(ctx, Start(t), GroupBy(…)); client.GetBilling, client.UpdateBilling(ctx, Plan("paid")), client.GetBalance, client.UpdateBalance(ctx, AutoReload(true)), client.TopUp(ctx, 20), client.ListCharges and client.ListLedger. The project, its events and the bill are a control plane’s.

The data of an event. Every type of event the control plane has has a struct of its data in this package, named after the description’s schema of it, which e.Decode(&v) reads (var made glac.EventDatabase; err := e.Decode(&made)). A database’s: glac.EventDatabase (database.created, with its region and PostgreSQL version; database.deleted, with final_snapshot_id, the snapshot of its Drive kept seven days), glac.EventDatabaseBranch (database.branch_created, with its parent; database.branch_deleted, with its reason), glac.EventDatabaseRestored (database.restored: the moment at, and old_branch_id, the branch the state before is kept as), glac.EventDatabaseArchive (database.archive_failing: since, how many segments in a row failures, archived_until, how far a restore reaches meanwhile, and the object store’s error; database.archive_recovered: recovered_at) and glac.EventDatabaseGap. The others likewise: a sandbox’s (glac.EventSandboxSlept, glac.EventSandboxWoke, glac.EventSandboxMoved, glac.EventSandboxDeleted, …), an app’s and its keys’ (glac.EventApp, glac.EventAppFailed, glac.EventKeyCreated, …), the project’s deploys and key, backups, domains, destinations and Drives’ snapshots (glac.EventProjectDeployed, glac.EventBackup, glac.EventDomain, glac.EventDestination, glac.EventDriveSnapshot, …), and the account’s plan, top-ups, refunds, withdrawals, balance, Credits, storage, egress cap, spend limits, hold and debts (glac.EventSubscriptionPeriod, glac.EventTopupCollected, glac.EventWithdrawalReceived, glac.EventBalance, glac.EventInDebt, …). A field that not every event of a type has, or that is null, is its zero value (a pointer, where zero would say something else).

Webhooks, API keys, spend limits, the project. client.Webhooks.Create(ctx, url, glac.WebhookEvents(…), glac.WebhookEnabled(…)) sends the project’s events to an address of yours (the Secret that signs each delivery is in that answer alone); Get, List, Update(ctx, id, glac.WebhookURL(…)…), Delete. client.Keys.Create(ctx, glac.ProjectKeyName(…), glac.ProjectKeyScopes(…), glac.ProjectKeyExpiresAt(t)) makes an API key (the key itself, Key, is in that answer alone); Get, List, Delete. client.Limits(ctx) and client.UpdateLimits(ctx, glac.SpendLimit(50)) (glac.NoSpendLimit()) are the project’s spend limit and what was spent this month, and sbx.Limits, sbx.UpdateLimits a sandbox’s own. client.RequestLimit(ctx, limit, wanted, why) asks for one of the account’s limits raised on request (a cap by its name, "volumes" or "volume_bytes"; bytes of the two in bytes; 409 limit_request_pending while one of the same limit waits), and client.ListLimitRequests(ctx, glac.PageSize(n), glac.After(cursor)) and client.LimitRequest(ctx, id) read them with the operator’s answers (*LimitRequest: Status pending, granted or refused, Granted, Answer). The data of instance.restarted, instance.upgraded, instance.reverted, instance.upgrade_failed and limit_request.granted and .refused events decode into EventInstanceRestarted, EventInstanceUpgraded, EventInstanceReverted, EventInstanceUpgradeFailed and EventLimitRequest; a sandbox’s (and an instance’s) Info().StartCommand says how its start command runs, how often it was started again in its machine, and how it last ended. client.UpdateProject(ctx, glac.DeleteAfterIdleDays(30)) (glac.NeverDeleteIdle()) sets after how many days asleep its sandboxes are deleted. A control plane’s. Webhooks and API keys need a key with the admin scope, and so do setting the project’s limit and UpdateProject; setting a sandbox’s limit needs write.

Log destinations, the audit log, the billing documents. client.Destinations is where the logs (and the metrics and the requests’ traces: glac.DestinationSignals("logs", "traces")) of the project’s apps are exported (OpenTelemetry endpoints): Create(ctx, name, glac.DestinationEndpoint(u), glac.DestinationHeaders(h)), which tries it at once with one record (Status.Connected), Get, List, Update, Delete, Test (one record now); a header’s value is a credential, sent when a destination is made or changed and in no answer (what is returned has the headers’ names alone). client.ListAudit(ctx, glac.PageSize(n)) is a page of the project’s audit log, the newest first (who, what method, what path, what status): every call that was no reading, a refused one too, and every reading of an app’s log by an operator. client.GetStatement(ctx, "2026-09") is a month’s statement (“” is this month’s) with each database’s own line in it (Databases: what it used in the month, its history and its tiered data apart, and what that comes to at the list prices), client.ListInvoices(ctx) the invoices at Stripe (with the PDF’s link), client.ListNotices(ctx) the mail the account was sent, client.ListWithdrawals(ctx) what it may withdraw from now and the withdrawals it made (a consumer in the EEA or the UK may withdraw from a purchase within 14 days), and client.Withdraw(ctx, chargeID, name, email) the buyer’s own statement of withdrawal, received at once. A control plane’s. Making, changing, testing and deleting a destination, reading the audit log and withdrawing need a key with the admin scope; the rest, one that may read.

bill, err := client.GetBilling(ctx)
if err != nil {
	log.Fatal(err)
}
cpu := bill.Meters.CPU
fmt.Println(bill.Plan, cpu.Used, cpu.Unit, "used") // free 7.5 cpu_seconds used

The account’s Email is where its notices go, which glac.AccountEmail(address) sets: EmailVerified says whether it is verified (only a verified address is mailed: one that is not gets a link first), and EmailUndeliverable (an *EmailUndeliverable: Why, Since, Detail) that mail to it bounced; meanwhile the notices wait (where notices go). The account’s Language is the language its notices are written in (a NoticeLanguage: Code “en”, “de” or “fr”, and Source “account”, “billing_address” or “default”); client.UpdateAccount(ctx, glac.AccountLanguage("de")) chooses it, and glac.NoAccountLanguage() takes the choice back, so that the billing address says again. The acknowledgement of a withdrawal and the confirmation of a purchase are written in it; every other notice is English.

The account’s Keeps (an AccountKeeps, behind a control plane) is what the key’s project keeps of Drives and volumes, by the numbers a create is held to (403 drive_limit_reached, volume_limit_reached), each with what the project has: Volumes (a KeepsCount: Limit, Used, and Source, “plan” or “operator”), VolumeBytes (a KeepsSize: how large one may be, and its Source), VolumesBytes (a KeepsTotal: how large all of them may be together, Limit nil for no limit, and Used) and UnsizedBytes, the size of one made with no size. A number of the account’s own (“operator”) is asked for with client.RequestLimit(ctx, "volumes", …) or "volume_bytes".

Buying at once (StartNow). A consumer in the European Economic Area or the United Kingdom (an account with no tax id that is verified or being verified) may withdraw from a purchase within 14 days, and what it buys is provided at once only at its own express request. glac.StartNow() carries that request: client.TopUp(ctx, 20, glac.StartNow()), client.UpdateBilling(ctx, glac.Plan("paid"), glac.StartNow()) (Paid, bought from Free) and client.UpdateBalance(ctx, glac.AutoReload(true), glac.StartNow()) (for every auto reload while auto reload stays on), in the words the console shows beside its box (and GET /v1/billing/withdrawals has, right.start_now):

I ask for this purchase to be provided at once, within the 14 days in which I may withdraw from it. If I withdraw, I pay for what was provided until then. I understand that once all the Credits of a top-up are used, it has been provided in full and I can no longer withdraw from it.

The service starts now; within the 14 days the buyer may still withdraw, and pays for what was provided until then; once it is fully provided (a top-up whose Credits are all used), the right to withdraw is lost. Give it only when the person buying asks for that: the SDK never sends it unless it is given. Without it, such an account is refused: an *Error of Status 400, Code “start_now_required”, Param “start_now”, in words that name its country and quote the request, and nothing is bought. An account whose auto reload is on without the request reloads nothing, and is told once a month (the event auto_reload.no_request, read with EventAutoReloadNoRequest; an account with no card on record, auto_reload.no_card and EventAutoReloadNoCard).

Errors

A non-zero exit code is a result: read res.ExitCode. An error of the API is a *glac.Error with Status, Type, Code, Param, Retryable, RequestID and Message, and DocsURL, which leads to the code on the errors page. No connection, a timeout or an ended context is a *glac.Error too, with Status 0; errors.Is(err, context.DeadlineExceeded) still works. Errors about money (402 account_in_debt, 403 trust_limit_exceeded, 409 no_card, 400 start_now_required, …) carry the control plane’s own message, which says what lifts them.

var e *glac.Error
if errors.As(err, &e) {
	fmt.Println(e.Status, e.Code, e.RequestID)
}

Behaviour worth knowing

  • Sleeping sandboxes. No call fails because a sandbox sleeps or is starting: it waits and goes on. res.Woke and res.Wake say whether Run woke it and how long that took; OnResponse(f) on a client sees the same for every call.
  • Retries. No answer, or a retryable error, is retried twice (Retries(n)), from 0.2 s doubling. Every POST carries an Idempotency-Key; IdempotencyKey(key) lets a program that restarts get back the sandbox, command or version it made before.
  • Timeouts. A call lasts until its context ends; RequestTimeout (15 minutes unless you say) is how long to wait for the API. Timeout(d) is the command’s limit: it is killed and Status is timed_out.
  • Durations and nulls. Durations and times are time.Duration and time.Time. Where the API’s null means something, there is an option for it (NeverSleep(), NoName(), NoEgress(), NoSleepNotice(), DefaultSleepHoldMax()), and an option not given changes nothing.
  • Goroutines. Client (its products’ services with it), Sandbox, Command and Files may be used from several goroutines.
Navigasi

Ketik untuk mencari…

↑↓ navigasi↵ pilihEsc tutup