Skip to content

Getting started

This page sets up a Linux workstation to build any Inky Labs project. You need two things: Nix, which supplies the pinned tools a build runs, and Bazelisk, which runs the version of Bazel that each repository asks for. Builds then read the shared cache automatically. To send changes you also need glab and the fork workflow; an administrator adds AWS and Porkbun access.

Use the official multi-user (daemon) installer. It needs sudo and asks a few questions, so run it in a real terminal:

Terminal window
sh <(curl -L https://nixos.org/nix/install) --daemon

Open a new terminal afterwards so your shell picks up the Nix profile, then check it:

Terminal window
nix --version

Bazel uses Nix only to fetch tools from nixpkgs at the versions a repository pins. You don’t need to learn Nix to build a project.

Bazelisk is a single binary. It reads .bazelversion in the repository you’re in, then downloads and runs that Bazel release. Install it on your PATH under the name bazel:

Terminal window
version=v1.29.0
url=https://github.com/bazelbuild/bazelisk/releases/download/$version/bazelisk-linux-amd64
mkdir -p ~/.local/bin
curl -fsSL -o ~/.local/bin/bazelisk "$url"
echo "$(curl -fsSL "$url.sha256") $HOME/.local/bin/bazelisk" | sha256sum -c -
chmod +x ~/.local/bin/bazelisk
ln -sf bazelisk ~/.local/bin/bazel

Check it. Outside a repository this prints the latest Bazel release. Inside one, it prints the version pinned in .bazelversion:

Terminal window
bazel --version

No Inky Labs repository builds with Bazel yet, so there is nothing to try this on until one is converted. From the root of any converted repository:

Terminal window
bazel build //... # build every cacheable output
bazel test //... # run every test Bazel knows about
bazel run //:assets # in a repository with generated files: install them,
# and run the steps that can't be cached

A repository that needs other tools (usually Node, for npm ci and its tests) pins them in shell.nix: run nix-shell at its root to get them. Its AGENTS.md says if anything must come first, such as npm ci. See Builds and CI.

Build only what you need by naming a target, for example bazel build //:site. Bazel works out what that target depends on and builds only that.

On Ubuntu, Bazel reports processwrapper-sandbox instead of linux-sandbox, because AppArmor restricts the user namespaces the Linux sandbox needs. That’s expected.

Nothing else needs to be installed. If a build seems to need a program from your machine’s PATH, that’s a bug in the build. Report it rather than installing the program.

Bazel reads the shared cache at https://bazel-cache.inkylabs.com (each repository’s .bazelrc says so), so anything CI has already built comes down instead of being rebuilt. The summary line shows it: … remote cache hit. The host exists, but until CI uploads to it a build finds few remote hits.

glab is GitLab’s CLI. inkylabs-mr and the examples in this handbook use it. Install it from nixpkgs and log in once, choosing gitlab.com and HTTPS as the Git protocol:

Terminal window
nix --extra-experimental-features 'nix-command flakes' profile add nixpkgs#glab
glab auth login
glab auth status

Then let Git use it for gitlab.com credentials, so HTTPS clones and pushes need no SSH key or password:

Terminal window
git config --global credential.https://gitlab.com.helper ''
git config --global --add credential.https://gitlab.com.helper '!glab auth git-credential'

You need to be a member of the inkylabs group; ask an owner to add you (Getting access).

Install the shared tools from this handbook’s repository (Tools), inkylabs-mr among them. Every change is then a merge request from the project’s fork, never a push to the project (Source code):

Terminal window
git clone https://gitlab.com/inkylabs/<project>.git # the project, not the fork
cd <project>
git switch -c <topic> # …commit…
git fetch origin && git rebase origin/master # merges are fast-forward
inkylabs-mr # push to inkylabs/forks/<project>, open the merge request

Not yet set up for Inky Labs: the inkylabs/forks group, the review bot and the handbook’s own project don’t exist yet, so inkylabs-mr has nowhere to push. Until they do, Inky Labs projects take changes the way they always have. A few projects’ default branch is main, not master (edit-server, kid-years, remark-fancyquote); rebase onto that.

Only for an Inky Labs administrator’s own machine, with full-disk encryption (see Builds and CI for what trusted means). These steps let it upload what it builds to the shared cache. Every other machine stops at step 4.

The inkylabs AWS account exists (created 2026-10-26), but only its administrator can sign in so far: ask an owner for a user.

5. Install the AWS CLI, from nixpkgs:

Terminal window
nix --extra-experimental-features 'nix-command flakes' profile add nixpkgs#awscli2
aws --version

6. Point it at the Inky Labs access portal. Add this to ~/.aws/config (create the file if it doesn’t exist):

[sso-session inkylabs]
sso_start_url = https://d-906662109b.awsapps.com/start
sso_region = us-east-1
sso_registration_scopes = sso:account:access
[profile inkylabs]
sso_session = inkylabs
sso_account_id = 889952605315
sso_role_name = AdministratorAccess
region = us-west-2

Then sign in. This opens a browser tab to approve, and lasts about a working day:

Terminal window
aws sso login --profile inkylabs
aws sts get-caller-identity --profile inkylabs # shows your user name

7. Install bazel-cache-sync from this handbook’s repository (see Tools):

Terminal window
ln -sf ~/projects/developers.inkylabs.com/tools/bazel-cache-sync ~/.local/bin/bazel-cache-sync

8. Upload after a build. Build as usual, then:

Terminal window
AWS_PROFILE=inkylabs bazel-cache-sync --dry-run # what would go up
AWS_PROFILE=inkylabs bazel-cache-sync

inkylabs-mr does this for you before it pushes a merge request, after building and testing the commit, so its pipeline finds everything already done; it uploads only that build’s results. By hand, bazel-cache-sync uploads your whole disk cache, uncommitted experiments included, so prefer inkylabs-mr. Every other machine trusts what you upload.

Only for an administrator who manages Inky Labs’ domains and DNS (Domains). Inky Labs’ domains, inkylabs.com among them, are in the default Porkbun account, not a subaccount, so porkbun reaches them without --profile. Create an API key pair under Account → API Access in Porkbun’s control panel, turn on API Access for each domain you’ll manage, and put the keys in ~/.env, never in a repository:

Terminal window
PORKBUN_API_KEY=pk1_…
PORKBUN_SECRET_KEY=sk1_…

Then check:

Terminal window
porkbun ping # shows the account the keys act as
porkbun dns list inkylabs.com

These keys reach more than Inky Labs. The default account also holds personal domains (latilde.org, symbolics.dev, matchingwell.com, biblepod.app and others), and a key pair sees every domain in its account. Keep them to trusted workstations, and think twice before handing them to CI. Porkbun’s per-domain API Access switch may limit what a key can touch; unverified: check that a key is refused for a domain with API access off before relying on it.

  • Builds and CI: why the setup looks like this, and the rules every repository follows.
  • Source code: forks, owners, the review bot, and who may merge.