taubyte-go-sdk-constraints
Go SDK Constraints
Use this skill for any Go function implementation or debugging.
Go function filesystem layout (non-negotiable)
- Authoring layout: after
tau new function --template empty --language Go, all hand-written Go for the function lives inempty.goat the function root — the same directory asgo.mod(e.g.code/functions/<name>/empty.go). Do not putempty.gounderlib/or any subdirectory for source you maintain. - Never manually create
lib/, never move or copyempty.gointolib/, and never hand-authormain.go(or any extra.goshims) in the function tree to “fix” the build. That pattern duplicates packages, confuses the WASM layout, and breakstau build function/ CI (e.g. found packages lib and main in/src/lib). - Do not “normalize” the tree: no merging a second
main.gounderlib/, no relocating the scaffold out of the root. - If
tau build functionor a local experiment once droppedmain.goor alib/tree into the repo, treat those as mistakes or stale artifacts when they contradict the root-empty.gorule: remove straylib/and hand-curatedmain.go, keep only rootempty.go(+go.mod/.taubyte/*as generated). Do not recreatelib/to satisfy the compiler.
Local WASM build test (preferred over ad-hoc tau build tree hacks)
From the function root (directory containing empty.go and go.mod), with Docker available, use taubyte/go-wasi so the official /utils/wasm.sh path runs against a clean /src copy — not by adding lib/ or main.go locally.
mkdir -p out
docker run -it --rm \
-e CODE=/src \
-v "$(pwd)/out:/out" \
--mount type=bind,src="$(pwd)",dst=/src_ro,ro \
--mount type=tmpfs,dst=/src \
taubyte/go-wasi /bin/bash -c '
set -e
rsync -a --delete /src_ro/ /src/ 2>/dev/null || cp -a /src_ro/. /src/
echo "export CODE=/src; cd /src; source /utils/wasm.sh" > /tmp/cowrc
exec bash --rcfile /tmp/cowrc -i'
CODE=/srcis required./utils/wasm.shrunsgo run . ${CODE}/lib lib; ifCODEis empty,${CODE}/libbecomes/liband you get package lib not found.mv .gitinwasm.shmay print cannot stat '.git' when there is no.gitin the function folder; that is harmless.- Run interactively inside the container, then run
build ./(or your target path). Artifacts land under./outon the host (artifact.wasm,_artifact.wasm). - Non-interactive one-shot (same mounts; no TTY): after
cdto the function root,source /utils/wasm.shthenbuild ./in the samebash -cstring (omitset -eifmv .gitwould abort your wrapper). - On Windows, use Git Bash with
MSYS_NO_PATHCONV=1in front ofdockerso paths like/outare not rewritten; use$(pwd -W)for bind mounts if Docker Desktop needs a Windows path.
HTTP event constraints
h.Headers().Get(key)returns(string, error)(two variables).h.Query().Get(key)returns(string, error)(two variables).- Do not use
h.URL().Query()...; useh.Query().Get(...). - Response ordering: set headers (including
Content-Typefor JSON), write the body (h.Write(...)), then callh.Return(status)last. CallingReturnbeforeWriteyields empty or truncated bodies and brokenresponse.json()clients.
PubSub event constraints (consumer — trigger.type: pubsub)
- From the PubSub event object:
Data()returns([]byte, error)(two variables);Channel()returns(*ChannelObject, error)(two variables). Typical pattern:pubsubEvent, err := e.PubSub()thenpubsubEvent.Data()/pubsubEvent.Channel()— align with your handler’s generated event parameter name. - Optionally verify
Channel().Name()equals the configured channel when multiple channels exist. - Deserialize
Data(), validate, then optionally open KV withdatabase.New("<match>")andPut/ other side effects.
PubSub producer and WebSocket (HTTP handlers — pubsub/node)
import pubsubnode "github.com/taubyte/go-sdk/pubsub/node".ch, err := pubsubnode.Channel("<channelName>")— the string must equal the messaging resource’schannel.matchand any PubSub function’strigger.channelin the same project wiring.ch.Publish(payloadBytes)to broadcast from an HTTP handler (check errors in real code).- For browser realtime:
url, err := ch.WebSocket().Url()and returnurl.String()(e.g. JSON) to the client; messaging YAML should havebridges.websocket.enable: truewhen using WebSocket URLs.
Cross-wiring checklist: messaging.channel.match == trigger.channel on PubSub functions == pubsubnode.Channel("..."); keep messaging.local and trigger.local aligned when both exist; execution.call must match //export in the built WASM.
Storage constraints
- Retrieve storage via
storage.Get(match)orstorage.New(match). - Write with
stor.File(fileName).Add(data, overwrite). - Read with:
file := stor.File(fileName)sf, err := file.GetFile()- then
sf.Read(...)orio.Copy(...)andsf.Close().
- Do not use invalid patterns like
stor.New().File(...).
Database (KV) — github.com/taubyte/go-sdk/database
import "github.com/taubyte/go-sdk/database".db, err := database.New("<match>")—<match>must equal the database resource’s YAMLmatchwhenuseRegex: false(not the file name ordescription). Path-style matchers are exact: e.g. YAMLmatch: /todosrequiresdatabase.New("/todos")including the leading/.defer db.Close()when the DB is opened for the scope of one request or event.Put(key, []byte),Get(key)→([]byte, error),Delete(key),List("<prefix>")→ keys under a prefix (e.g. listtodo/thenGeteach). Sort in application code if you need stable ordering.- Missing keys:
Getfailing or empty value is often normal (first run, unknown id). For list APIs, treat “no index key yet” as an empty list, not necessarily HTTP 500 — reserve 500 for real store errors. - Key design: namespace with prefixes (
todo/<id>,note/<id>, …). Prefer one database resource per app with a stablematchand many prefixes unless policy requires splitting.
Quick patterns
name, _ := h.Headers().Get("X-File-Name")
name, _ = h.Query().Get("name")
data, err := ev.Data()
if err != nil { return 1 }
_, err = stor.File("filename.txt").Add(body, true)
file := stor.File("filename.txt")
sf, err := file.GetFile()
defer sf.Close()
Database (import "github.com/taubyte/go-sdk/database"; match must match YAML):
db, err := database.New("appdata")
if err != nil { return 1 }
defer db.Close()
_ = db.Put("todo/1", []byte(`{"title":"x"}`))
raw, err := db.Get("todo/1")
keys, err := db.List("todo/")
Go WASM libraries (not the empty.go function scaffold)
Library resources are separate repos: they ship .taubyte/ + go-sdk and expose handlers with //export <Symbol> comments. Config functions/*.yaml then sets source: libraries/<library> and execution.call: <Symbol> per route — one build, many HTTP resources (see taubyte-resource-creation and taubyte-reference-index → Full-stack worked example).
- The “root
empty.goonly / never hand-createlib/” rule applies totau new function --template emptytrees, not to authoring conventions inside an external library repo the platform builds as WASM. - Use the same HTTP and database API shapes as above (
h.Query().Get,database.Newpath matching thedatabasesmatchin config, etc.). libraries/*.yamlsource.pathmust match the directory layout inside the library’s GitHub repo that actually contains the built Go sources (often repo root/). Wrongsource.path→ wrong or empty WASM.- Keep response bodies and
Content-Typeconsistent with the front-end (e.g. JSON vs raw bytes) soresponse.json()matches what the handler writes; write body thenReturn(see HTTP constraints above).
More from taubyte/skills
verifying-taubyte-functions
Verifies a Taubyte Go function locally via the `taubyte/go-wasi` Docker recipe (preferred over `tau build`, with tmpfs+bind-mount-ro to avoid root-owned artifacts in the source tree), and verifies a function actually serves on Dream by curling the gateway with the right `Host:` header (plus `/etc/hosts` mapping for `*.localtau`). Use when locally compiling a Go function to WASM, when smoke-testing a function before pushing, or when probing a Dream-hosted HTTP function from the laptop.
12creating-taubyte-resources
Creates Taubyte resources non-interactively via `tau new` for domain, website, library, function, application, database, storage, messaging, and service. Encodes the project-vs-application scope rule, the database `min < max` constraint, the website/library `--generate-repository` + import sequence, and the forbidden `--generated-fqdn-prefix` flag. Use when adding any resource to a Taubyte project's config repo.
12diagnosing-dream-builds
Diagnoses Dream local-cloud builds when `tau list/query builds` is empty or unreliable, by hitting the jobs HTTP endpoint directly (`GET /jobs/<project_id>`, `GET /job/<job_id>`) using the GitHub token from `~/tau.yaml`, then downloading logs with `tau query logs --jid`. Use when Dream builds appear silent, the build table is empty after `dream inject`, or you need raw job ids and logs for a failing build.
11taubyte-resource-creation
Scope-aware resource creation workflow. Uses non-interactive mode by default and references the shared flags catalog.
11taubyte-push-build-verify
Pushes config/code and verifies builds/logs. Includes website/library push handling with tau command first, git fallback.
11taubyte-scope-routing
Routes project-level vs application-scoped work; defaults to a website when a browser UI is logically appropriate; avoids unnecessary applications for simple website/function-only tasks unless needed.
11