deploy-taubyte-spore-drive
Deploy Taubyte cloud (Spore Drive boilerplate)
End-to-end workflow matches the upstream repo: https://github.com/taubyte/spore-drive-boilerplate
Execute steps in order. Prefer running commands in the user’s environment rather than only describing them.
Prerequisites
- Node.js and npm
- Linux servers with SSH on port 22, sudo-capable user, key-based SSH
- A DNS zone the user controls for
ROOT_DOMAIN(and parent zone ifROOT_DOMAINis a subdomain)
1. Clone the boilerplate
git clone https://github.com/taubyte/spore-drive-boilerplate.git
cd spore-drive-boilerplate
2. Create hosts.csv
Path defaults to hosts.csv (overridable via SERVERS_CSV_PATH).
Format (header row required; column names must match):
hostname— FQDN of each node (e.g.node1.mycloud.com)public_ip— public IPv4 of that node
One row per Taubyte node; multi-node clouds add multiple rows.
Example:
hostname,public_ip
node1.mycloud.com,203.0.113.1
node2.mycloud.com,203.0.113.2
3. SSH key path
In .env, set SSH_KEY to the filesystem path of the private key used to reach every host (same key as the boilerplate README; not the pubkey).
Ensure permissions are restrictive (e.g. chmod 600 on the key file) if SSH complains.
4. Environment file (ask the user; skip Namecheap)
cp .env.example .env
Collect values conversationally and write .env. Do not require or prompt for Namecheap variables (NAMECHEAP_API_KEY, NAMECHEAP_IP, NAMECHEAP_USERNAME, NAMECHEAP_DOMAIN) unless the user explicitly wants API-managed DNS.
Variables to set (mirror .env.example / README):
| Variable | Purpose |
|---|---|
SSH_KEY |
Path to SSH private key |
SERVERS_CSV_PATH |
CSV path (default hosts.csv) |
SSH_USER |
SSH login user (default ssh-user) |
ROOT_DOMAIN |
Platform root domain (e.g. example.com) |
GENERATED_DOMAIN |
Generated subdomain for the platform; typically g.<ROOT_DOMAIN> or another label under ROOT_DOMAIN |
Convention from README: GENERATED_DOMAIN should be a subdomain of ROOT_DOMAIN (e.g. ROOT_DOMAIN=pom.ac, GENERATED_DOMAIN=g.pom.ac). If Namecheap vars are all empty/unset, src/index.ts skips API DNS and the user must add records manually (see below).
5. Install and deploy
npm install
npm run displace
displace runs tsx src/index.ts: applies Spore Drive config, displaces the course, then runs fixDNS. With no Namecheap credentials, expect [Skip] DNS Records — that is expected when using manual DNS.
6. Manual DNS records (logic from src/index.ts)
When Namecheap vars are not all set, fixDNS returns false and no records are pushed. Reproduce the same logical records in the user’s DNS dashboard.
Derived from fixDNS (and the zoneLines it builds):
6.1 Names used in the simple case
Assume the DNS zone apex is ROOT_DOMAIN (e.g. ROOT_DOMAIN=example.com and the user manages the example.com zone).
Let:
generatedPrefix= label part ofGENERATED_DOMAINbefore.ROOT_DOMAIN
Example:GENERATED_DOMAIN=g.example.com→generatedPrefix=g.
If GENERATED_DOMAIN does not end with .${ROOT_DOMAIN}, the script uses the full GENERATED_DOMAIN string as generatedPrefix — prefer the README pattern g.<ROOT_DOMAIN> to avoid ambiguity.
6.2 Records to create
-
A —
seer.<ROOT_DOMAIN>
One A record per node IP: same name (seer/seer.<ROOT_DOMAIN>), each address is a distinctpublic_ipfrom hosts that participate in shapeall(in practice, each row inhosts.csvafter a successful deploy). Multiple A records on the same name match Spore-drive’ssetAll(seerHost, "A", seerAddrs)behavior. -
NS —
tau.<ROOT_DOMAIN>
Nameserver target:seer.<ROOT_DOMAIN>(must be the hostname that has the A records above). In zone-file form the target appears asseer.<ROOT_DOMAIN>.(FQDN). -
CNAME —
*.<generatedPrefix>.<ROOT_DOMAIN>
Example:*.g.example.comwhengeneratedPrefix=g.
Target (alias):substrate.tau.<ROOT_DOMAIN>(e.g.substrate.tau.example.com).
6.3 When ROOT_DOMAIN is not the zone apex
If ROOT_DOMAIN is a subdomain of the registered zone (e.g. ROOT_DOMAIN=tau.example.com while the registrar zone is example.com), the relative host labels in the boilerplate shift:
subPrefix= part ofROOT_DOMAINbefore.<apex>(e.g.taufortau.example.comunder apexexample.com).- A: names like
seer.<subPrefix>under zone apex → e.g.seer.tau.example.com. - NS:
tau.<subPrefix>under apex → e.g.tau.tau.example.com→ NS targetseer.<ROOT_DOMAIN>.(i.e.seer.tau.example.com.). - CNAME:
*.+generatedPrefix+.+subPrefixunder apex → e.g.*.g.tau.example.com→ targetsubstrate.tau.<ROOT_DOMAIN>..
If unsure which case applies, resolve the zone apex (SOA for ROOT_DOMAIN) or ask which domain is the registrar / DNS provider zone vs platform subdomain.
6.4 What to tell the user
After a successful npm run displace, output a short checklist:
- List each A for
seerwith the concrete IPs from theirhosts.csv(or from committed host addresses if known). - The NS on
tau.<ROOT_DOMAIN>pointing atseer.<ROOT_DOMAIN>. - The wildcard CNAME for
*.<generatedPrefix>.<ROOT_DOMAIN>→substrate.tau.<ROOT_DOMAIN>. - TTL: upstream uses 1800 seconds where applicable; any reasonable TTL is fine.
Remind them to add records in the zone that actually serves ROOT_DOMAIN (registrar or DNS host).
Optional: Namecheap automation
If the user later sets all of NAMECHEAP_USERNAME, NAMECHEAP_API_KEY, and NAMECHEAP_IP, re-running the script’s DNS step can push records automatically; NAMECHEAP_DOMAIN is optional (SOA discovery otherwise). This skill’s default path is manual DNS without those variables.
Troubleshooting pointers
- CSV errors: column names must be exactly
hostnameandpublic_ip(src/csv.ts). - SSH:
SSH_USERmust match a user with sudo on every host;SSH_KEYmust be readable. - Displacement errors: progress output lists failing host/task pairs.
- DNS skip: empty Namecheap env trio →
[Skip] DNS Records; add manual records from section 6.
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