docs(guides): CLI install guides — forge/cloud/devops/edge/iot/embedded-linux (24 guides + index, self-hosted-first policy)

This commit is contained in:
fabiorafaelcoutada 2026-07-12 20:51:27 +01:00
parent 912cf45b51
commit 8e5ba6ba93
26 changed files with 2357 additions and 0 deletions

96
docs/guides/README.md Normal file
View file

@ -0,0 +1,96 @@
# CLI Guides
Install, authenticate, and configure the command-line tools used across the
Portugal Futurista fleet. Every guide follows the same shape:
**What it is → Install → Authenticate → Configure for this environment →
Self-hosted equivalent → Aurélio integration → Verify → Troubleshooting**.
## Self-hosted first policy
Default to self-hosted endpoints in dev. Public clouds only when required.
Concretely:
- **Forge** — [Forgejo](https://code.portugalfuturista.org) (LAN `192.168.0.9:3001`)
is the default. `github.com` is for public mirrors only; use `tea`, not `gh`.
- **Object storage** — MinIO on `http://192.168.0.40:9000` (console `:9001`),
S3-compatible. Point `aws s3`, Terraform/OpenTofu state, and backup jobs here first.
- **AWS APIs** — LocalStack on `http://192.168.0.40:4566` emulates the common
AWS services. Develop against it; switch profiles to real AWS only for
cloud-specific behaviour.
- **Azure Storage** — Azurite on `192.168.0.40` (blob `:10000`, queue `:10001`,
table `:10002`) via connection strings.
- **Secrets** — never in shell history or dotfiles. Store API tokens in
Vaultwarden (https://vault.portugalfuturista.org, see
[docs/vaultwarden-setup.md](../vaultwarden-setup.md)) and reference them from
`~/.config` files with `chmod 600`.
## Self-hosted services
| Guide | What it covers |
|-------|----------------|
| [cloud-emulators](selfhosted/cloud-emulators.md) | MinIO + LocalStack v3 + Azurite on lattepanda (`192.168.0.40`) — deploy/manage, credentials, CLI wiring, Terraform state backend |
All guides target the laptop: **Linux x86_64, Debian/Ubuntu-based, bash**
(arm64 variants noted where relevant).
## Guides
### Core (forge & collaboration)
| Guide | Tool | One-liner | Self-hosted equivalent |
|-------|------|-----------|------------------------|
| [gh](cli/gh.md) | GitHub CLI | PRs/issues/releases on github.com (public mirrors only) | Forgejo → use `tea` instead |
| [tea](cli/tea.md) | Gitea/Forgejo CLI | **Primary forge tool** for code.portugalfuturista.org | It *is* the self-hosted tool |
| [acli](cli/acli.md) | Atlassian CLI | Jira + Confluence from the terminal | None deployed (Plane/OpenProject deferred) |
### DevOps
| Guide | Tool | One-liner | Self-hosted equivalent |
|-------|------|-----------|------------------------|
| [aws](cli/aws.md) | AWS CLI v2 | AWS API access, profiles | LocalStack `:4566` + MinIO `:9000` |
| [gcloud](cli/gcloud.md) | Google Cloud CLI | GCP projects, GKE, GCS | None real; Firebase emulators / fake-gcs-server |
| [az](cli/az.md) | Azure CLI | Azure resources + storage | Azurite `:10000-10002` via connection strings |
| [kubectl](cli/kubectl.md) | Kubernetes CLI | Cluster ops | k3s on Proxmox |
| [helm](cli/helm.md) | Helm | Kubernetes package manager | Same — points at the k3s cluster |
| [terraform-opentofu](cli/terraform-opentofu.md) | Terraform / OpenTofu | IaC; prefer OpenTofu (MPL-2.0) | State on MinIO via S3 backend |
| [docker-compose](cli/docker-compose.md) | Docker Engine + Compose | Containers on the laptop | Docker itself is the self-hosted runtime |
| [ansible](cli/ansible.md) | Ansible | Agentless config of Proxmox nodes | It *is* self-hosted (inventory = LAN nodes) |
### Edge / serverless
| Guide | Tool | One-liner | Self-hosted equivalent |
|-------|------|-----------|------------------------|
| [cloudflared-wrangler](cli/cloudflared-wrangler.md) | cloudflared + wrangler | Cloudflare tunnel `pf-tunnel-v2` + Workers | Tunnel already self-hosted on asus |
| [vercel](cli/vercel.md) | Vercel CLI | Deploy previews / serverless frontends | None deployed (Coolify on Proxmox = future) |
| [netlify](cli/netlify.md) | Netlify CLI | Static + functions deploys | None deployed; Caddy behind pf-tunnel-v2 |
| [flyctl](cli/flyctl.md) | Fly.io CLI | Global container deploys | None; k3s/Kamal on Proxmox |
| [doctl](cli/doctl.md) | DigitalOcean CLI | DO droplets/spaces | Proxmox cluster (`pct`/`qm`) is the self-hosted equivalent |
### IoT
| Guide | Tool | One-liner | Self-hosted equivalent |
|-------|------|-----------|------------------------|
| [mosquitto-clients](cli/mosquitto-clients.md) | mosquitto-clients | MQTT pub/sub from the shell | mosquitto broker LXC on Proxmox |
### Embedded Linux
| Guide | Tool | One-liner | Self-hosted equivalent |
|-------|------|-----------|------------------------|
| [esptool](cli/esptool.md) | esptool.py | ESP32 flash/serial | Local tool — no cloud dependency |
| [arduino-cli](cli/arduino-cli.md) | Arduino CLI | Compile/upload Arduino sketches | Local tool — no cloud dependency |
| [platformio](cli/platformio.md) | PlatformIO Core | Multi-platform embedded builds | Local tool; registry mirrorable |
| [yocto](cli/yocto.md) | Yocto / poky + kas | Custom Linux images | Build in crops/poky container; Proxmox CT later |
| [buildroot](cli/buildroot.md) | Buildroot | Minimal embedded Linux images | Fully local by design |
| [uboot-tools](cli/uboot-tools.md) | u-boot-tools | `mkimage` for U-Boot images | Fully local by design |
| [cross-toolchains](cli/cross-toolchains.md) | gcc-arm-none-eabi, aarch64-linux-gnu, west | Cross-compile for ARM targets | Fully local by design |
## Infra reference
| Host | IP | Role |
|------|-----|------|
| asus | 192.168.0.38 | Proxmox (CTs 202220), cloudflared `pf-tunnel-v2` |
| gigabyte | 192.168.0.104 | Proxmox (CTs 200211), media stack, signal-cli |
| lattepanda | 192.168.0.40 | Proxmox node; cloud emulators (MinIO, LocalStack, Azurite) |
| Forgejo | 192.168.0.9:3001 / code.portugalfuturista.org | Default git forge |
| Vaultwarden | vault.portugalfuturista.org | Secrets (see [setup](../vaultwarden-setup.md)) |

82
docs/guides/cli/acli.md Normal file
View file

@ -0,0 +1,82 @@
# acli (Atlassian CLI)
## What it is
Atlassian's official CLI (`acli`) for Jira and Confluence from the terminal:
search issues, create/transition tickets, read and edit Confluence pages.
## Install
Linux x86_64 — official binary (always the latest stable; see the
[Atlassian CLI docs](https://developer.atlassian.com/cloud/acli/guides/introduction/)
for the canonical link):
```bash
curl -LO "https://acli.atlassian.com/linux/latest/acli_linux_amd64/acli"
chmod +x acli
sudo mv acli /usr/local/bin/acli
```
On arm64 use the `acli_linux_arm64` variant of the same URL. The CLI
self-updates via `acli update` unless you disable it.
## Authenticate
```bash
acli jira auth login
```
Browser-based OAuth 2.0 (3LO) against your Atlassian cloud site; the token is
cached under `~/.config/atlassian-cli/`. Keep a note of which Atlassian
account/org you authorized in Vaultwarden so onboarding a second laptop is a
one-minute job.
## Configure for this environment
```bash
acli jira workitem search --jql "project = PF AND status != Done" --limit 10
acli confluence page view --id <page-id>
```
Set a default site once so commands don't prompt:
```bash
acli jira site list # pick your site
```
## Self-hosted equivalent
None deployed yet. Self-hosted Jira/Confluence replacements
(**Plane**, **OpenProject**) were evaluated and deferred — when one lands on
the Proxmox cluster it will be announced, and it won't be `acli`-compatible
(Plane and OpenProject have their own REST APIs; closest dev stand-in today is
the Plane community Docker compose stack).
## Aurélio integration
The **cli-atlassian** skill drives `acli` for Jira/Confluence queries and
ticket updates. Connector registry id: `atlassian`
(OAuth 2.0 3LO, `api.atlassian.com`).
## Verify
```bash
acli --version
# acli version x.y.z
acli jira auth status
# ✓ Authenticated as <name> (<site>.atlassian.net)
acli jira workitem search --jql "assignee = currentUser()" --limit 3
# KEY SUMMARY STATUS ...
```
## Troubleshooting
- **Browser flow fails on a headless shell** — run `acli jira auth login` from
a desktop session, or copy the printed URL to a browser and paste the
callback back.
- **Wrong site** — you authorized multiple Atlassian sites; pass `--site` or
re-login with the right tenant.
- **403 on Jira API** — missing OAuth scope; re-run `auth login` and accept the
full scope list.
- **Corporate proxy breaking OAuth callback** — set `HTTPS_PROXY` before
running `acli`, or use the API-token auth fallback for Jira REST scripts.

View file

@ -0,0 +1,89 @@
# ansible (Ansible CLI)
## What it is
Agentless configuration management over SSH: `ansible` for ad-hoc commands,
`ansible-playbook` for playbooks. Ideal for keeping the Proxmox nodes and CTs
consistent.
## Install
Via pipx (current stable, isolated env — preferred over the lagging distro
package):
```bash
sudo apt install pipx
pipx install --include-deps ansible-core
pipx ensurepath # re-login so ~/.local/bin is on PATH
```
Or the distro package if you don't need recent versions: `sudo apt install
ansible`. Extra collections (e.g. `community.general`) via
`ansible-galaxy collection install <name>`.
## Authenticate
Auth = SSH. Use your existing key:
```bash
ssh-copy-id root@192.168.0.38 # asus
ssh-copy-id root@192.168.0.104 # gigabyte
ssh-copy-id root@192.168.0.40 # lattepanda
```
Vault-stored secrets (sudo passwords, API tokens) go in Vaultwarden
(https://vault.portugalfuturista.org); for playbook-level secrets use
`ansible-vault encrypt_string`.
## Configure for this environment
Inventory `~/.ansible/inventory/proxmox.ini`:
```ini
[proxmox]
asus ansible_host=192.168.0.38
gigabyte ansible_host=192.168.0.104
lattepanda ansible_host=192.168.0.40
[proxmox:vars]
ansible_user=root
ansible_python_interpreter=/usr/bin/python3
```
Ad-hoc check:
```bash
ansible -i ~/.ansible/inventory/proxmox.ini proxmox -m ping
ansible -i ~/.ansible/inventory/proxmox.ini proxmox -a "pveversion"
```
## Self-hosted equivalent
Ansible is inherently self-hosted — controller on the laptop, targets on the
LAN. No cloud service involved; the closest "managed" analogue (AWX/Tower) is
overkill for three nodes.
## Aurélio integration
The **cli-devops** skill uses `ansible` for fleet maintenance tasks against
the Proxmox nodes. Connector registry id: `ansible`.
## Verify
```bash
ansible --version
# ansible [core 2.x.y] ...
ansible -i ~/.ansible/inventory/proxmox.ini asus -m ping
# asus | SUCCESS => { "ping": "pong" }
```
## Troubleshooting
- **`UNREACHABLE`** — SSH key not installed on the node, or you're not on the
LAN/VPN; test with plain `ssh root@<ip>`.
- **`/usr/bin/python: not found`** — minimal Debian CTs ship only `python3`;
set `ansible_python_interpreter=/usr/bin/python3` (as above).
- **Privilege escalation prompts** — add `-b --ask-become-pass` or configure
passwordless sudo on managed CTs.
- **Slow runs** — enable SSH pipelining and `forks = 20` in
`~/.ansible.cfg`.

View file

@ -0,0 +1,83 @@
# arduino-cli (Arduino CLI)
## What it is
Arduino's headless build tool: manage cores (board support packages) and
libraries, compile sketches, and upload over serial — no IDE required. Good
fit for CI builds of Arduino-based firmware.
## Install
Official script (latest stable into `~/.local/bin`):
```bash
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh \
| BINDIR=~/.local/bin sh
```
Static binaries (x86_64/arm64) also on the releases page if you want to pin a
version. Add `~/.local/bin` to PATH if it isn't already.
## Authenticate
None for core functionality. Only the (optional) Arduino Cloud integration
uses credentials — we don't use it; keep any such tokens in Vaultwarden if you
ever enable it.
## Configure for this environment
First-run setup for AVR + ESP32:
```bash
arduino-cli config init
arduino-cli config add board_manager.additional_urls \
https://espressif.github.io/arduino-esp32/package_esp32_index.json
arduino-cli core update-index
arduino-cli core install arduino:avr
arduino-cli core install esp32:esp32
arduino-cli lib install "PubSubClient"
```
Serial access: `sudo usermod -aG dialout $USER` (re-login).
Typical cycle:
```bash
arduino-cli board list # find the port + FQBN
arduino-cli compile --fqbn esp32:esp32:esp32 sketch/
arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:esp32 sketch/
arduino-cli monitor -p /dev/ttyUSB0 -c baudrate=115200
```
## Self-hosted equivalent
Fully local — cores/libraries download from public indexes, but builds and
uploads happen on the laptop. No cloud dependency. (Build artifacts can be
mirrored to MinIO `http://192.168.0.40:9000` with the rest of the firmware.)
## Aurélio integration
The **embedded-linux** skill uses `arduino-cli` for Arduino-core firmware
builds and uploads (ESP32/AVR). Flashed devices usually talk to the
`mqtt-local` broker connector.
## Verify
```bash
arduino-cli version
# arduino-cli Version: 1.x.y ...
arduino-cli board list
# Port Protocol Type Board Name FQBN Core
# /dev/ttyUSB0 serial Serial Port (USB) Unknown esp32:esp32:... esp32:esp32
```
## Troubleshooting
- **`Board not found` on compile** — the core isn't installed;
`arduino-cli core search <chip>` then `core install`.
- **ESP32 package index ignored** — the `additional_urls` must be added via
`config add` (as above), then `core update-index` again.
- **Upload permission denied**`dialout` group missing, or `arduino-cli
monitor`/minicom still holding the port.
- **Compile cache confusion after core upgrade**`arduino-cli cache clean`
then rebuild.

106
docs/guides/cli/aws.md Normal file
View file

@ -0,0 +1,106 @@
# aws (AWS CLI v2)
## What it is
The official AWS command line (v2): S3, IAM, Lambda, IoT Core, CloudFormation —
every AWS API from the shell, with named profiles for multi-account work.
## Install
Official Linux x86_64 installer (always current stable; use the `-aarch64` URL
on arm64):
```bash
curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o /tmp/awscliv2.zip
unzip -q /tmp/awscliv2.zip -d /tmp
sudo /tmp/aws/install # add --update to upgrade in place
```
Do **not** use the distro `awscli` package — it is usually the legacy v1.
## Authenticate
```bash
aws configure --profile prod
# AWS Access Key ID: <from Vaultwarden>
# AWS Secret Access Key: <from Vaultwarden>
# Default region name: eu-west-1
# Default output format: json
```
Credentials live in `~/.aws/credentials` (chmod 600). Keep the root copies of
all access keys in Vaultwarden (https://vault.portugalfuturista.org); rotate
rather than leak.
## Configure for this environment
`~/.aws/config` — real AWS plus the local emulators:
```ini
[default]
region = eu-west-1
output = json
[profile prod]
region = eu-west-1
[profile localstack]
region = us-east-1
output = json
```
`~/.aws/credentials`:
```ini
[localstack]
aws_access_key_id = test
aws_secret_access_key = test
```
## Self-hosted equivalent
**LocalStack** (AWS emulator, lattepanda `http://192.168.0.40:4566`) and
**MinIO** (S3-compatible, `http://192.168.0.40:9000`, console `:9001`).
```bash
# LocalStack: everything behind one endpoint
aws --profile localstack --endpoint-url http://192.168.0.40:4566 s3 ls
aws --profile localstack --endpoint-url http://192.168.0.40:4566 lambda list-functions
# MinIO: S3 only
aws --profile localstack --endpoint-url http://192.168.0.40:9000 s3 mb s3://firmware
aws --profile localstack --endpoint-url http://192.168.0.40:9000 s3 cp build.bin s3://firmware/
```
For MinIO use its own access/secret keys (console at `:9001`), stored in
Vaultwarden — not `test/test`.
Alternative: `pipx install awscli-local` gives `awslocal`, which wraps `aws`
with the LocalStack endpoint pre-set (`awslocal s3 ls`).
## Aurélio integration
The **cli-aws** skill wraps `aws` (and `awslocal`) for infra and IoT Core
tasks. Connector registry ids: `aws` (real), `aws-localstack` (emulator),
`minio` (S3 storage).
## Verify
```bash
aws --version
# aws-cli/2.x.y Python/3.x Linux/x86_64 ...
aws --profile localstack --endpoint-url http://192.168.0.40:4566 sts get-caller-identity
# { "Account": "000000000000", "Arn": "arn:aws:iam::000000000000:root", ... }
```
## Troubleshooting
- **`aws: command not found` after install** — installer puts it in
`/usr/local/bin`; check your `PATH`.
- **Signature errors against MinIO** — wrong keys or clock skew; verify keys in
the MinIO console and that the laptop clock is NTP-synced.
- **`Connection refused` to LocalStack** — the emulator stack on lattepanda
(192.168.0.40) isn't up; check its Docker stack before debugging `aws`.
- **Real AWS used by accident** — always pass `--profile localstack
--endpoint-url ...` in dev scripts; export `AWS_PROFILE=localstack` in your
dev shell as a seatbelt.

90
docs/guides/cli/az.md Normal file
View file

@ -0,0 +1,90 @@
# az (Azure CLI)
## What it is
Microsoft's CLI for Azure resources (`az group`, `az vm`, `az storage`,
`az iot`, ...). One login covers all Azure services.
## Install
Official Microsoft apt repo:
```bash
curl -sLS https://packages.microsoft.com/keys/microsoft.asc \
| sudo gpg --dearmor -o /etc/apt/keyrings/microsoft.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/microsoft.gpg] \
https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main" \
| sudo tee /etc/apt/sources.list.d/azure-cli.list
sudo apt update && sudo apt install azure-cli
```
(On arm64 use `arch=arm64`.) The one-liner `curl -sL
https://aka.ms/InstallAzureCLIDeb | sudo bash` does the same but pins less —
the explicit repo above is preferred.
## Authenticate
```bash
az login # browser flow
az account set --subscription <name-or-id>
```
Token cache: `~/.azure/`. For automation, create a service principal and keep
the client secret in Vaultwarden (https://vault.portugalfuturista.org):
```bash
az login --service-principal -u <app-id> -p <secret-from-vaultwarden> --tenant <tenant-id>
```
## Configure for this environment
```bash
az config set defaults.group=<resource-group> defaults.location=westeurope
az account list --output table
```
## Self-hosted equivalent
**Azurite** — the Azure Storage emulator on lattepanda
(`192.168.0.40`: blob `:10000`, queue `:10001`, table `:10002`). Point `az
storage` at it with a connection string:
```bash
export AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=http;\
AccountName=devstoreaccount1;\
AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;\
BlobEndpoint=http://192.168.0.40:10000/devstoreaccount1;\
QueueEndpoint=http://192.168.0.40:10001/devstoreaccount1;\
TableEndpoint=http://192.168.0.40:10002/devstoreaccount1;"
az storage container create --name firmware
az storage blob upload --container-name firmware --file build.bin --name build.bin
```
(The account key above is Azurite's well-known dev key, not a secret.)
There is no emulator for non-storage Azure services — use real Azure for those.
## Aurélio integration
The **cli-azure** skill wraps `az` for resource and storage ops. Connector
registry ids: `azure` (real), `azure-azurite` (storage emulator).
## Verify
```bash
az version
# "azure-cli": "2.x.y", ...
az account show --output table
# Name CloudName SubscriptionId State IsDefault
```
## Troubleshooting
- **`az login` opens no browser** — use `az login --use-device-code` and
complete the flow at microsoft.com/devicelogin.
- **Connection string ignored**`az storage` flags (`--account-name`) win
over the env var; unset conflicting flags or export
`AZURE_STORAGE_ACCOUNT`/`AZURE_STORAGE_KEY` instead.
- **Azurite SSL errors** — Azurite here is plain HTTP; make sure the
connection string uses `DefaultEndpointsProtocol=http`.
- **Wrong tenant**`az account clear` then `az login --tenant <id>`.

View file

@ -0,0 +1,79 @@
# buildroot (Buildroot)
## What it is
Buildroot builds minimal embedded Linux systems (cross toolchain, rootfs,
kernel, bootloader) with a Kconfig interface — much lighter than Yocto for
simple appliances. One `make` produces a flashable image.
## Install
Host dependencies (Debian/Ubuntu):
```bash
sudo apt install build-essential git libncurses-dev bison flex \
libssl-dev bc rsync unzip wget cpio file python3
```
Buildroot itself is just a git clone (pin a release branch or tag, e.g.
`2024.02.x` LTS or newer — check buildroot.org for the current LTS):
```bash
git clone -b 2024.02.x https://github.com/buildroot/buildroot.git ~/buildroot
# or shallow: git clone --depth 1 -b <branch> ...
```
## Authenticate
None. Everything is local; packages download from upstream mirrors over HTTPS.
## Configure for this environment
```bash
cd ~/buildroot
make list-defconfigs | grep -i <board> # e.g. raspberrypi, beaglebone
make <board>_defconfig
make menuconfig # adjust packages/kernel/rootfs
make -j"$(nproc)" # ~30-60 min first build
ls output/images/ # sdcard.img, zImage, rootfs.tar...
```
Keep project-specific configs as **external trees** (BR2_EXTERNAL) in the
firmware repo on Forgejo — never fork Buildroot itself:
```
BR2_EXTERNAL=../my-overlay make myboard_defconfig
```
Archive release images to MinIO (`http://192.168.0.40:9000`) with the defconfig
that produced them.
## Self-hosted equivalent
Fully self-hosted by design — no cloud services anywhere in the loop. For
reproducible offline builds, set `BR2_PRIMARY_SITE` to an internal mirror and
commit the downloaded `dl/` cache to MinIO.
## Aurélio integration
The **embedded-linux** skill uses Buildroot for minimal appliance images.
Resulting images are deployed with the [uboot-tools](uboot-tools.md) /
`swupdate` flow and talk to the `mqtt-local` connector at runtime.
## Verify
```bash
cd ~/buildroot && make show-info | head -5 # prints configured packages as JSON
make <board>_defconfig && make -j"$(nproc)" linux
# ... after a full build: output/images/ contains the artifacts
```
## Troubleshooting
- **Missing host tool error at `make`** — install the package named in the
error; `make` checks the host first (see deps above).
- **Defconfig for your board missing** — look in `configs/` upstream or the
board vendor's BR2_EXTERNAL layer; don't hand-write one from scratch.
- **`make menuconfig` can't find ncurses** — `sudo apt install libncurses-dev`.
- **Build breaks after `git pull`** — rebuild from clean: `make clean && make
<board>_defconfig && make`; Buildroot doesn't support in-tree upgrades.

View file

@ -0,0 +1,97 @@
# cloudflared & wrangler (Cloudflare tunnel + Workers CLIs)
## What it is
Two Cloudflare CLIs: **cloudflared** runs/manages tunnels (our
`pf-tunnel-v2` exposes the `*.portugalfuturista.org` services), and
**wrangler** builds and deploys Cloudflare Workers/Pages.
## Install
cloudflared (official apt repo):
```bash
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \
| sudo tee /usr/share/keyrings/cloudflare-main.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] \
https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" \
| sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared
```
wrangler (via npm, current stable):
```bash
npm install -g wrangler
```
## Authenticate
```bash
cloudflared tunnel login # browser → authorizes the zone, writes ~/.cloudflared/cert.pem
wrangler login # browser OAuth for your Cloudflare account
```
API tokens (for CI) go in Vaultwarden (https://vault.portugalfuturista.org)
and are exported as `CLOUDFLARE_API_TOKEN`.
## Configure for this environment
`pf-tunnel-v2` **runs on asus** (192.168.0.38), config at
`/etc/cloudflared/config-v2.yml` — tunnel administration happens there, not
on the laptop. From the laptop you typically only:
```bash
cloudflared tunnel list # see pf-tunnel-v2 and its connectors
cloudflared tunnel info pf-tunnel-v2
cloudflared tunnel route dns pf-tunnel-v2 new-service.portugalfuturista.org
```
Adding a service = edit `config-v2.yml` on asus (ingress rule) + DNS route,
then restart the cloudflared service on asus.
wrangler for Workers:
```bash
wrangler init my-worker
cd my-worker && wrangler dev # local dev server
wrangler deploy
```
## Self-hosted equivalent
The tunnel **is** our self-hosted edge: `pf-tunnel-v2` on asus fronts all
`*.portugalfuturista.org` services — no third-party ingress needed. Workers
have no self-hosted runtime in production; for local dev `wrangler dev` (and
Miniflare under the hood) emulates the Workers runtime on the laptop.
## Aurélio integration
The **cli-edge** skill uses `cloudflared` for tunnel/DNS routes and `wrangler`
for Workers deploys. Connector registry id: `cloudflare`.
## Verify
```bash
cloudflared --version
# cloudflared version 20xx.y.z ...
wrangler --version
# ⛅️ wrangler x.y.z
cloudflared tunnel list
# ID NAME CREATED CONNECTIONS
# ... pf-tunnel-v2 ... 2xLISB
```
## Troubleshooting
- **`tunnel login` cert lands on the wrong machine** — the cert is per-user,
per-machine; run `cloudflared tunnel login` on asus if you're managing the
tunnel there, then copy `~/.cloudflared/<uuid>.json` credentials carefully.
- **DNS route already exists**`cloudflared tunnel route dns` refuses to
overwrite; delete the CNAME in the dashboard or with `cloudflared tunnel
route dns --overwrite-dns`.
- **wrangler `Unauthorized`** — token scope too narrow; create a token with
"Workers Scripts:Edit" and re-export `CLOUDFLARE_API_TOKEN`.
- **Service 502 behind the tunnel** — check the ingress rule in
`config-v2.yml` on asus points at a reachable LAN address, then
`journalctl -u cloudflared` on asus.

View file

@ -0,0 +1,95 @@
# cross-toolchains (ARM GCC + Zephyr west)
## What it is
Cross-compilers that run on the laptop (x86_64) and target embedded ARM:
`gcc-arm-none-eabi` (bare-metal Cortex-M), `aarch64-linux-gnu` (64-bit ARM
Linux userland/kernel), and Zephyr's `west` (meta-tool for Zephyr RTOS
builds).
## Install
Debian/Ubuntu:
```bash
# Bare-metal ARM (Cortex-M): distro package is fine for most work
sudo apt install gcc-arm-none-eabi binutils-arm-none-eabi
# aarch64 Linux cross toolchain
sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
# Zephyr west + build deps
sudo apt install pipx python3-venv cmake ninja-build gperf ccache dfu-util \
device-tree-compiler xz-utils file make gcc g++ multilib gcc-multilib
pipx install west
pipx ensurepath # re-login
```
Note: distro `gcc-arm-none-eabi` can lag or be huge (newlib variants). The
Arm GNU Toolchain tarball from developer.arm.com is the pin-able alternative —
extract to `/opt` and add its `bin/` to PATH when a project requires a
specific version.
## Authenticate
None. Zephyr modules fetch from public git (Forgejo mirrors where available:
`code.portugalfuturista.org`).
## Configure for this environment
Zephyr workspace:
```bash
west init -m https://github.com/zephyrproject-rtos/zephyr --mr main ~/zephyrproject
cd ~/zephyrproject && west update
west zephyr-export
pipx runpip west install -r zephyr/scripts/requirements.txt # python deps
```
(Alternatively install the Zephyr SDK tarball for its bundled toolchains, or
set `ZEPHYR_TOOLCHAIN_VARIANT=cross-compile` +
`CROSS_COMPILE=/usr/bin/arm-none-eabi-` to reuse the distro GCC.)
Kernel cross-compile example:
```bash
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j"$(nproc)"
```
## Self-hosted equivalent
Fully local — no cloud involved. Zephyr module fetches and toolchain tarballs
can be mirrored to MinIO (`http://192.168.0.40:9000`) for CI reproducibility.
## Aurélio integration
The **embedded-linux** skill uses these toolchains for bare-metal and RTOS
firmware (`west build`, kernel cross-compiles). Firmware targets the
`mqtt-local` broker at runtime.
## Verify
```bash
arm-none-eabi-gcc --version | head -1
# arm-none-eabi-gcc (...) x.y.z ...
aarch64-linux-gnu-gcc --version | head -1
# aarch64-linux-gnu-gcc (Debian ...) x.y.z ...
west --version
# West version: v1.x.y
west boards | grep -i nrf52840 | head -3 # in a zephyr workspace
# nrf52840dk/nrf52840 ...
```
## Troubleshooting
- **`arm-none-eabi-gcc: command not found` in make** — toolchain bin dir not
on PATH in that shell (common when mixing tarball + distro installs).
- **Newlib missing (`nano.specs` errors)** — install
`libnewlib-arm-none-eabi` (and `libstdc++-arm-none-eabi-newlib` for C++).
- **`west build` can't find the Zephyr SDK** — either install the SDK or
point `ZEPHYR_TOOLCHAIN_VARIANT`/`CROSS_COMPILE` at the distro toolchain as
above.
- **Wrong architecture triplet**`aarch64-linux-gnu-` for 64-bit ARM Linux,
`arm-linux-gnueabihf-` for 32-bit hard-float, `arm-none-eabi-` for
bare-metal; mixing them produces very confusing linker errors.

View file

@ -0,0 +1,95 @@
# docker compose (Docker Engine + Compose plugin)
## What it is
Docker Engine runs containers on the laptop; the Compose plugin (`docker
compose`, v2) brings up multi-container stacks from a `compose.yaml`. This is
the local dev runtime for everything — including the MinIO/LocalStack/Azurite
emulators.
## Install
Official Docker apt repo (Debian/Ubuntu; the `docker.io` distro package lags):
```bash
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
| sudo tee /etc/apt/sources.list.d/docker.list
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER # re-login to pick up the group
```
(On Debian use `download.docker.com/linux/debian`.) The repo always serves the
current stable channel; pin with `apt install docker-ce=5:<version>` if needed.
## Authenticate
Only for registries:
```bash
docker login # Docker Hub (PAT, not password)
docker login code.portugalfuturista.org # if a Forgejo package registry is used
```
Tokens are in `~/.docker/config.json` (base64 — treat it as secret; keep the
PATs themselves in Vaultwarden, https://vault.portugalfuturista.org).
## Configure for this environment
Compose overrides for LAN services live in `compose.override.yaml` (gitignored).
Typical dev stack pointing at the emulators on lattepanda:
```yaml
services:
app:
build: .
environment:
AWS_ENDPOINT_URL: http://192.168.0.40:4566
S3_ENDPOINT: http://192.168.0.40:9000
ports: ["8080:8080"]
```
```bash
docker compose up -d
docker compose ps
docker compose logs -f app
```
## Self-hosted equivalent
Docker **is** the self-hosted runtime — no cloud dependency. The cloud
counterpart would be managed container services (ECS/Cloud Run/ACI), which we
only touch when a deployment target requires it.
## Aurélio integration
The **cli-devops** skill shells out to `docker compose` for local stack
management. No dedicated connector-registry entry — Docker is the local
runtime the other connectors (minio, aws-localstack, azure-azurite) run on.
## Verify
```bash
docker --version
# Docker version 2x.y.z, build ...
docker compose version
# Docker Compose version v2.x.y
docker run --rm hello-world
# Hello from Docker! ...
```
## Troubleshooting
- **Permission denied on `/var/run/docker.sock`** — you're not in the `docker`
group (or haven't re-logged in since `usermod`).
- **`docker-compose: command not found`** — that's v1 syntax; use the plugin:
`docker compose ...`.
- **Disk full**`docker system df`, then `docker system prune -a` (drops
unused images; emulator volumes survive unless you add `--volumes`).
- **CT/VM can't reach images** — Docker Hub rate limits: `docker login` with a
Hub account, or pull through a mirror.

80
docs/guides/cli/doctl.md Normal file
View file

@ -0,0 +1,80 @@
# doctl (DigitalOcean CLI)
## What it is
DigitalOcean's CLI: droplets, Kubernetes (DOKS), Spaces (S3-compatible), App
Platform, container registry — everything in the DO API from the shell.
## Install
GitHub release binary (x86_64; use the `arm64` archive on ARM):
```bash
VERSION=$(curl -fsSL https://api.github.com/repos/digitalocean/doctl/releases/latest \
| grep -oP '"tag_name":\s*"v\K[^"]+')
curl -fsSL -o /tmp/doctl.tar.gz \
"https://github.com/digitalocean/doctl/releases/download/v${VERSION}/doctl-${VERSION}-linux-amd64.tar.gz"
tar -xzf /tmp/doctl.tar.gz -C /tmp
sudo install /tmp/doctl /usr/local/bin/doctl
```
(Also available as a snap, but the static binary pins cleanly and has no snap
confinement surprises.)
## Authenticate
Create a personal access token in the DO dashboard (read+write), store it in
Vaultwarden (https://vault.portugalfuturista.org), then:
```bash
doctl auth init # paste the token
doctl auth list
```
Config: `~/.config/doctl/config.yaml`. Multiple tokens →
`doctl auth init --context <name>` and `doctl auth switch`.
## Configure for this environment
```bash
doctl account get
doctl compute droplet list
doctl kubernetes cluster list
doctl registry login # docker creds for registry.digitalocean.com
```
## Self-hosted equivalent
Our **Proxmox cluster is the self-hosted DigitalOcean**: droplets → VMs/CTs
managed with `qm`/`pct` (via SSH on asus/gigabyte/lattepanda), Spaces →
**MinIO** at `http://192.168.0.40:9000` (S3-compatible, works with `aws s3
--endpoint-url`), DOKS → planned **k3s** (see [kubectl.md](kubectl.md)).
`doctl` is used only for resources that must live outside the lab.
## Aurélio integration
The **cli-devops** skill can drive `doctl` for external VPS/registry ops. No
dedicated connector-registry entry — external SaaS; the self-hosted
counterparts are covered by `minio` and `kubernetes`.
## Verify
```bash
doctl version
# doctl version 1.x.y-release ...
doctl account get
# Email Droplet Limit Email Verified UUID Status
# you@... 25 true ... active
```
## Troubleshooting
- **`401 Unable to authenticate you`** — token expired/revoked; regenerate in
the dashboard, update Vaultwarden, `doctl auth init` again.
- **Wrong context active**`doctl auth list` then `doctl auth switch
--context <name>`.
- **Registry push denied** — run `doctl registry login` so Docker gets the
short-lived registry credentials.
- **Spaces endpoint confusion** — Spaces is S3-compatible; use `aws s3
--endpoint-url https://<region>.digitaloceanspaces.com` rather than looking
for a `doctl spaces` subcommand.

View file

@ -0,0 +1,75 @@
# esptool (Espressif flashing tool)
## What it is
`esptool.py` — Espressif's official tool for ESP32/ESP8266: flash firmware,
read flash, chip info, baud-tuned serial boot control. The standard last-mile
tool for ESP32 firmware.
## Install
Via pipx (isolated, current stable):
```bash
sudo apt install pipx python3-venv
pipx install esptool
pipx ensurepath # re-login so ~/.local/bin is on PATH
```
(`pip install esptool` in a venv works too. Needs Python 3.8+.)
USB serial access:
```bash
sudo usermod -aG dialout $USER # re-login to apply
```
## Authenticate
None — talks to the chip over USB/UART. No tokens involved.
## Configure for this environment
Devices appear as `/dev/ttyUSB0` (or `/dev/ttyACM0` for native-USB chips like
ESP32-S3). Convenience alias for the common fast-flash settings:
```bash
alias espflash='esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 460800'
```
For firmware destined at AWS IoT Core, keep device certificates in Vaultwarden
(https://vault.portugalfuturista.org) — never bake shared certs into images.
## Self-hosted equivalent
Fully local by design — flashing happens over USB on the laptop. Firmware
artifacts can be archived to MinIO (`http://192.168.0.40:9000`) for
reproducibility. For OTA at scale the self-hosted route is an update server
behind the LAN; cloud OTA (AWS IoT Jobs) only when devices are off-LAN.
## Aurélio integration
The **embedded-linux** skill uses `esptool` for ESP32 flash/verify cycles.
Flashed firmware typically targets the `mqtt-local` broker or `aws-iot`
connector endpoints.
## Verify
```bash
esptool.py version
# esptool.py v4.x / v5.x ...
esptool.py --port /dev/ttyUSB0 chip_id
# Chip is ESP32-D0WDQ6 (revision ...)
# MAC: xx:xx:xx:xx:xx:xx
```
## Troubleshooting
- **`Permission denied: /dev/ttyUSB0`** — not in `dialout` group, or a
lingering serial monitor (minicom/IDE) holds the port.
- **`Failed to connect`** — hold BOOT while plugging in (or press BOOT during
connect) to force download mode; also try a lower `--baud`.
- **Device not enumerated** — bad USB cable (charge-only) or missing driver
for the CP210x/CH340 bridge; check `dmesg | tail`.
- **Flash write errors at high baud** — drop to `--baud 115200`; some bridges
and cables can't sustain 460800+.

76
docs/guides/cli/flyctl.md Normal file
View file

@ -0,0 +1,76 @@
# flyctl (Fly.io CLI)
## What it is
Fly.io's CLI (`fly`/`flyctl`): deploy containers globally as "Machines",
manage volumes, WireGuard into your private Fly network, stream logs.
## Install
Official install script (current stable; also has arm64 builds):
```bash
curl -fsSL https://fly.io/install.sh | sh
```
It installs to `~/.fly/bin` and prints the PATH snippet to add to `~/.bashrc`
(`export FLYCTL_INSTALL="$HOME/.fly"; export PATH="$FLYCTL_INSTALL/bin:$PATH"`).
Upgrade later with `fly version upgrade`.
## Authenticate
```bash
fly auth login # browser OAuth
```
Token cached in `~/.fly/config.yml`. For CI, `fly auth token` prints a
deploy token — keep it in Vaultwarden (https://vault.portugalfuturista.org)
and export as `FLY_API_TOKEN`.
## Configure for this environment
```bash
cd <app>
fly launch # detects Dockerfile, creates fly.toml + app
fly deploy
fly logs
fly ssh console # shell into the machine
fly status # machines + regions
```
## Self-hosted equivalent
None deployed. The self-hosted counterpart for "run this container somewhere
reachable" is: Docker on a Proxmox CT exposed via the `pf-tunnel-v2` tunnel
(see [docker-compose.md](docker-compose.md) and
[cloudflared-wrangler.md](cloudflared-wrangler.md)), or **k3s** once the
cluster lands (see [kubectl.md](kubectl.md)). Fly stays in the toolbox for
genuinely global/multi-region workloads only.
## Aurélio integration
The **cli-edge** skill can use `flyctl` for global container deploys. No
dedicated connector-registry entry (external SaaS; nearest listed edge
connectors are `cloudflare` / `vercel`).
## Verify
```bash
fly version
# flyctl v0.x.y linux/amd64 ...
fly auth whoami
# Email: you@example.com
fly apps list
# NAME OWNER STATUS LATEST DEPLOY
```
## Troubleshooting
- **`fly` not found after install** — the PATH snippet wasn't added to
`~/.bashrc`; open a new shell or export it manually.
- **`Error: could not find app`** — run commands from the directory with
`fly.toml`, or pass `-a <app-name>`.
- **Deploy hangs on wireguard**`fly wireguard reset` or toggle the agent
with `fly agent restart`.
- **Health checks failing** — the app must bind `0.0.0.0` and the port in
`fly.toml` `internal_port`; check `fly logs` for the bind address.

91
docs/guides/cli/gcloud.md Normal file
View file

@ -0,0 +1,91 @@
# gcloud (Google Cloud CLI)
## What it is
Google Cloud's CLI: `gcloud` (projects, compute, IAM, GKE), bundled with
`gsutil` (Cloud Storage) and `bq` (BigQuery).
## Install
Official apt repo (Debian/Ubuntu):
```bash
sudo apt install apt-transport-https ca-certificates gnupg curl
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \
| sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" \
| sudo tee /etc/apt/sources.list.d/google-cloud-sdk.list
sudo apt update && sudo apt install google-cloud-cli
```
Alternative: the versioned tarballs from the
[install docs](https://cloud.google.com/sdk/docs/install) (`linux-x86_64` or
`linux-arm`); extract and run `./google-cloud-sdk/install.sh`.
## Authenticate
```bash
gcloud auth login # browser OAuth, user account
gcloud config set project <project-id>
gcloud auth application-default login # for SDKs/tools using ADC
```
Credentials land in `~/.config/gcloud/`. For service accounts, store the JSON
key in Vaultwarden (https://vault.portugalfuturista.org) and activate with:
```bash
gcloud auth activate-service-account --key-file=<path> --project=<project-id>
```
## Configure for this environment
Named configurations keep personal/lab projects apart:
```bash
gcloud config configurations create lab
gcloud config set project <lab-project-id>
gcloud config configurations list
gcloud config configurations activate lab
```
## Self-hosted equivalent
There is no real self-hosted GCP. Dev stand-ins we use instead:
- **Firebase Emulator Suite** (`firebase emulators:start`, via
`npm i -g firebase-tools`) for Firestore/Auth/Functions.
- **fake-gcs-server** for Cloud Storage:
`docker run -p 4443:4443 fsouza/fake-gcs-server -scheme http`, then
`STORAGE_EMULATOR_HOST=http://localhost:4443` for client libraries.
- For plain object storage, prefer **MinIO** (`http://192.168.0.40:9000`) —
S3-compatible and already on the LAN.
## Aurélio integration
The **cli-gcloud** skill wraps `gcloud`/`gsutil` for project and storage ops.
Connector registry id: `google-cloud`.
## Verify
```bash
gcloud --version
# Google Cloud SDK x.y.z
gcloud auth list
# Credentialed Accounts
# ACTIVE ACCOUNT
# * you@example.com
gcloud config list project
# [core]
# project = <project-id>
```
## Troubleshooting
- **Old SDK from distro packages** — only use the Google apt repo above;
distro `google-cloud-sdk` packages are frequently broken/stale.
- **`gcloud auth login` opens no browser** — run `gcloud auth login
--no-browser` and paste the code back.
- **ADC errors in scripts** — you authenticated `gcloud` but not
application-default credentials; run `gcloud auth application-default login`.
- **Wrong project billed** — check `gcloud config get-value project` before
provisioning; use named configurations.

96
docs/guides/cli/gh.md Normal file
View file

@ -0,0 +1,96 @@
# gh (GitHub CLI)
## What it is
GitHub's official CLI for pull requests, issues, releases, Actions runs, and
repo management against github.com and GitHub Enterprise hosts.
## Install
Debian/Ubuntu, official apt repo (recommended — tracks the stable channel):
```bash
sudo mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
| sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] \
https://cli.github.com/packages stable main" \
| sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update && sudo apt install gh
```
Alternative: download the `linux_amd64` tarball from the
[releases page](https://github.com/cli/cli/releases) and drop `bin/gh` into
`~/.local/bin`. Pin a version there if you need reproducibility across machines.
## Authenticate
```bash
gh auth login
```
Choose `GitHub.com``HTTPS` → browser flow. The token is stored in
`~/.config/gh/hosts.yml` (mode 600). Keep a copy of a fine-grained PAT in
Vaultwarden (https://vault.portugalfuturista.org) as the recovery secret;
prefer `GH_TOKEN` env var in CI instead of the interactive login.
## Configure for this environment
github.com is **not** the default forge here — Forgejo is (see
[tea.md](tea.md)). Use `gh` only for the public GitHub mirrors.
If you ever point `gh` at a GHE-style host:
```bash
export GH_HOST=git.example.com # per-shell default host
gh auth login --hostname git.example.com
```
Note: `GH_HOST` assumes a GitHub Enterprise API layout. Forgejo speaks the
Gitea API, so `gh` will not work against it — use `tea` instead.
```bash
gh config set git_protocol https
gh config set editor nvim
```
## Self-hosted equivalent
**Forgejo** at https://code.portugalfuturista.org (LAN `192.168.0.9:3001`) is
our default forge. The equivalent CLI is [`tea`](tea.md):
```bash
tea login add --url https://code.portugalfuturista.org
```
Everything you'd do with `gh pr create` maps to `tea pr create` against the
Forgejo remote.
## Aurélio integration
The **cli-forge** skill drives `gh`/`tea` for PR and issue workflows. The
connector registry (`.aurelio/connectors/registry.yaml`) covers github.com
under id `github`; the Forgejo instance is id `forgejo`.
## Verify
```bash
gh --version
# gh version 2.x.x (YYYY-MM-DD)
gh auth status
# github.com
# ✓ Logged in to github.com account <user> (...)
gh repo list --limit 3
# <owner>/<repo> <description> ...
```
## Troubleshooting
- **`gh` talks to the wrong host** — check `echo $GH_HOST`; unset it or pass
`--hostname github.com` explicitly.
- **Auth prompts in headless shells** — use `GH_TOKEN=<pat> gh ...` (token from
Vaultwarden) instead of the browser flow.
- **`error connecting to code.portugalfuturista.org`** — you used `gh` against
Forgejo; switch to `tea`.
- **Old version from distro repo** — Debian's `gh` package lags; use the
official apt repo above.

77
docs/guides/cli/helm.md Normal file
View file

@ -0,0 +1,77 @@
# helm (Helm CLI)
## What it is
Helm is the package manager for Kubernetes: charts bundle manifests, and
`helm install/upgrade/rollback` manages releases against the cluster your
kubeconfig points at.
## Install
Official install script (latest stable, detects arch):
```bash
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
```
Or the static binary: download `helm-<version>-linux-amd64.tar.gz` (or
`-linux-arm64`) from the [releases page](https://github.com/helm/helm/releases)
and move `linux-amd64/helm` to `/usr/local/bin`. Pin a version from the
releases page when you need identical clients across machines.
## Authenticate
No separate auth — Helm reuses your kubeconfig (`~/.kube/config`, see
[kubectl.md](kubectl.md)). Chart repos with private registries authenticate
via:
```bash
helm repo add <name> https://charts.example.com --username <u> --password <p>
```
Store repo credentials in Vaultwarden (https://vault.portugalfuturista.org).
## Configure for this environment
```bash
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm search repo bitnami | head
```
Point it at the self-hosted k3s cluster by setting `KUBECONFIG` (see
kubectl.md). Values overrides for LAN endpoints (MinIO etc.) go in a local
`values-lab.yaml` kept out of git.
## Self-hosted equivalent
Helm itself is infrastructure-neutral — the self-hosted part is the cluster it
targets (**k3s** on Proxmox, see kubectl.md) and, if you outgrow public chart
repos, a self-hosted **ChartMuseum** (which can store charts on MinIO at
`http://192.168.0.40:9000`). OCI registries (e.g. `helm push ... oci://...`)
are another option once a registry is deployed.
## Aurélio integration
The **cli-devops** skill drives `helm` for release management alongside
`kubectl`. Connector registry id: `kubernetes` (same cluster connector).
## Verify
```bash
helm version
# version.BuildInfo{Version:"v3.x.y", ...}
helm list --all-namespaces # against a configured cluster
# NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
```
## Troubleshooting
- **`Error: Kubernetes cluster unreachable`** — kubeconfig/context problem;
fix with `kubectl` first, Helm inherits it.
- **Chart not found after `helm repo add`** — run `helm repo update`.
- **Upgrade fails with "another operation in progress"** — a release is stuck
`pending-*`; `helm history <rel>` then `helm rollback <rel>`.
- **CRDs missing** — some charts (e.g. cert-manager) need CRDs installed first
(`helm install --set crds.enabled=true` or a separate CRD step); read the
chart's README.

View file

@ -0,0 +1,86 @@
# kubectl (Kubernetes CLI)
## What it is
`kubectl` is the Kubernetes control client: apply manifests, inspect workloads,
stream logs, exec into pods. It talks to any cluster via a kubeconfig.
## Install
Static binary, current stable (x86_64; swap `amd64`→`arm64` for ARM):
```bash
curl -fsSLO "https://dl.k8s.io/release/$(curl -fsSL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -m 0755 kubectl /usr/local/bin/kubectl && rm kubectl
```
Or the Google apt repo (`sudo apt install kubectl` after adding
`pkgs.k8s.io`). Keep the client within one minor version of the server.
## Authenticate
Auth is the kubeconfig, not a login command:
```bash
export KUBECONFIG=~/.kube/config # default
kubectl config get-contexts
kubectl config use-context <context>
```
Kubeconfigs are bearer secrets — keep them mode 600, and archive copies in
Vaultwarden (https://vault.portugalfuturista.org).
## Configure for this environment
No production cluster yet on the Proxmox fleet; the plan is k3s (below). For
cloud clusters, fetch credentials with the vendor CLI:
```bash
gcloud container clusters get-credentials <name> --zone <zone>
aws eks update-kubeconfig --name <name>
```
## Self-hosted equivalent
**k3s** — lightweight Kubernetes, ideal on a Proxmox CT/VM (asus or gigabyte):
```bash
# on the node (Debian/Ubuntu CT, privileged or with nesting enabled)
curl -sfL https://get.k3s.io | sh -
sudo cat /etc/rancher/k3s/k3s.yaml # copy to laptop ~/.kube/config
```
On the laptop, edit the `server:` line to `https://<node-ip>:6443` and:
```bash
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# k3s1 Ready control-plane,master 1m v1.x.y+k3s1
```
For a pure-laptop dev cluster, `k3d` (k3s in Docker) is the fastest option.
## Aurélio integration
The **cli-devops** skill uses `kubectl`/`helm` for cluster operations.
Connector registry id: `kubernetes`.
## Verify
```bash
kubectl version --client
# Client Version: v1.x.y ...
kubectl get --raw /readyz # against a configured cluster
# ok
```
## Troubleshooting
- **`connection refused` to 127.0.0.1:8080** — no context set; run
`kubectl config get-contexts` and `use-context`.
- **k3s kubeconfig 403 on the laptop** — you copied `k3s.yaml` but left
`server: https://127.0.0.1:6443`; point it at the node IP.
- **Version skew warnings** — keep kubectl within ±1 minor of the server;
reinstall from `dl.k8s.io/release/stable.txt`.
- **Proxmox CT can't run k3s** — enable nesting/keyctl or use an
unprivileged-with-features CT; plain VMs are the least-friction path.

View file

@ -0,0 +1,94 @@
# mosquitto-clients (MQTT pub/sub CLI)
## What it is
`mosquitto_pub` / `mosquitto_sub` — the reference MQTT command-line clients
from the Eclipse Mosquitto project. The quickest way to poke a broker from a
shell or script.
## Install
Debian/Ubuntu:
```bash
sudo apt install mosquitto-clients
```
(The `mosquitto` package is the broker — only install that on a broker host.)
## Authenticate
Broker-dependent. Typical username/password:
```bash
mosquitto_sub -h <broker> -t 'lab/#' -u <user> -P <password>
```
Keep broker credentials in Vaultwarden (https://vault.portugalfuturista.org).
For TLS brokers: `-p 8883 --cafile <ca.crt>` (+ `--cert/--key` for mutual TLS).
## Configure for this environment
Convenience env for the lab broker:
```bash
export MQTT_HOST=192.168.0.40 # or wherever the broker CT lands
export MQTT_PORT=1883
mosquitto_sub -h $MQTT_HOST -t 'lab/#' -v & # watch everything
mosquitto_pub -h $MQTT_HOST -t lab/test -m "hello"
```
## Self-hosted equivalent
The broker itself: **mosquitto in a Proxmox LXC** (Debian template):
```bash
# on the Proxmox host, in the CT console:
apt update && apt install mosquitto mosquitto-clients
systemctl enable --now mosquitto
```
Minimal hardening (`/etc/mosquitto/conf.d/lab.conf`):
```conf
listener 1883 0.0.0.0
allow_anonymous false
password_file /etc/mosquitto/passwd
```
```bash
mosquitto_passwd -c /etc/mosquitto/passwd labuser
systemctl restart mosquitto
```
(LAN-only, so no TLS required; add it if the broker ever leaves the LAN.)
Cloud MQTT (AWS IoT Core etc.) is only for production device fleets.
## Aurélio integration
The **cli-iot** skill uses `mosquitto_pub/sub` to probe device topics during
IoT bring-up. Connector registry ids: `mqtt-local` (LAN broker), `aws-iot`
(cloud counterpart).
## Verify
```bash
mosquitto_sub --help | head -3
# mosquitto_sub is a simple mqtt client ...
mosquitto_pub -h $MQTT_HOST -t lab/test -m ping
mosquitto_sub -h $MQTT_HOST -t lab/test -C 1 -W 5
# ping
```
## Troubleshooting
- **`Connection refused`** — broker not running, or listening on
`127.0.0.1` only (default on many distros); add `listener 1883 0.0.0.0`.
- **`Not authorized`** — anonymous access disabled; pass `-u/-P`, and check
the password file matches the config path.
- **Nothing received but publish succeeds** — topic mismatch; remember MQTT
wildcards: `+` single level, `#` multi-level (and `#` must be quoted in
bash).
- **TLS handshake failure** — wrong CA file or hostname mismatch; use
`--insecure` only as a temporary diagnostic, never in scripts.

View file

@ -0,0 +1,75 @@
# netlify (Netlify CLI)
## What it is
Netlify's CLI: deploy static sites and serverless functions, run the local dev
server (`netlify dev`) that emulates Netlify's edge/functions environment, and
manage sites/env from the terminal.
## Install
Via npm (current stable; Node LTS required):
```bash
npm install -g netlify-cli
```
## Authenticate
```bash
netlify login # browser OAuth
netlify status # shows account + site link
```
Token stored under `~/.config/netlify/`. For CI use a personal access token
from the dashboard, kept in Vaultwarden (https://vault.portugalfuturista.org),
exported as `NETLIFY_AUTH_TOKEN` (plus `NETLIFY_SITE_ID`).
## Configure for this environment
```bash
cd <site>
netlify init # or: netlify link
netlify dev # local server w/ functions emulation
netlify deploy # draft deploy, prints a preview URL
netlify deploy --prod # production
netlify env:list
```
## Self-hosted equivalent
None deployed. Closest self-hosted path for static + functions today:
**Caddy** serving a build output directory on a Proxmox CT, exposed through
the `pf-tunnel-v2` Cloudflare tunnel (route added via cloudflared — see
[cloudflared-wrangler.md](cloudflared-wrangler.md)). Netlify-style edge
functions have no drop-in self-hosted runtime; Cloudflare Workers + `wrangler
dev` is the nearest alternative.
## Aurélio integration
The **cli-edge** skill can drive `netlify` for legacy site deploys. No
dedicated connector-registry entry — treated as an external SaaS; closest
listed edge connector is `vercel`.
## Verify
```bash
netlify --version
# netlify-cli/1x.y.z linux-x64 node-v2x.y.z
netlify status
# ──────────────────────┐
# Current Netlify User │
# ... │
netlify sites:list | head
```
## Troubleshooting
- **`netlify login` hangs** — no browser on a headless shell; use
`NETLIFY_AUTH_TOKEN` from Vaultwarden instead.
- **Functions work locally, 404 in prod** — check `netlify.toml`
`[functions] directory` and that the deploy actually bundled them
(`netlify deploy --prod --build` logs show "Functions bundling").
- **Redirects not applied**`_redirects` must end up in the publish dir;
verify with `netlify deploy --prod` output listing published files.
- **Wrong site linked**`netlify unlink` then `netlify link` again.

View file

@ -0,0 +1,89 @@
# platformio (PlatformIO Core)
## What it is
PlatformIO Core (`pio`) — cross-platform embedded build system: one
`platformio.ini` pins the platform, board, framework (Arduino/ESP-IDF/Zephyr),
and library deps. The default for multi-board firmware projects.
## Install
Via pipx (isolated, current stable):
```bash
sudo apt install pipx python3-venv
pipx install platformio
pipx ensurepath # re-login so ~/.local/bin is on PATH
```
Serial access + udev rules for common boards:
```bash
sudo usermod -aG dialout $USER
curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/system/99-platformio-udev.rules \
| sudo tee /etc/udev/rules.d/99-platformio-udev.rules
sudo udevadm control --reload-rules && sudo udevadm trigger
```
## Authenticate
Core features need no login. `pio account` (registry publishing, remote
builds) is optional — skip it; if ever used, token goes in Vaultwarden.
## Configure for this environment
Example `platformio.ini` for an ESP32 lab node:
```ini
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
upload_port = /dev/ttyUSB0
lib_deps =
knolleary/PubSubClient @ ^2.8
```
```bash
pio run # build all envs
pio run -t upload # flash
pio device monitor # serial
pio run -t clean
```
## Self-hosted equivalent
Builds are fully local. The PlatformIO Registry is a cloud service, but deps
are cached in `~/.platformio/` and can be vendored into the repo
(`lib_deps = symlink://lib/...` or committed libraries) for offline
reproducibility; firmware artifacts archive to MinIO
(`http://192.168.0.40:9000`).
## Aurélio integration
The **embedded-linux** skill drives `pio` for firmware build/upload/monitor
cycles. Devices built here typically target the `mqtt-local` broker or
`aws-iot` connectors.
## Verify
```bash
pio --version
# PlatformIO Core, version 6.x.y
pio device list
# /dev/ttyUSB0 ... USB Serial
pio run
# ... SUCCESS ... RAM: [...] Flash: [...]
```
## Troubleshooting
- **First build downloads a lot** — platforms/toolchains install into
`~/.platformio/` on demand; keep that dir cached between CI runs.
- **Upload fails, port busy** — close `pio device monitor` (or the IDE
serial monitor) before flashing.
- **Board not auto-detected** — set `upload_port` explicitly in
`platformio.ini` (as above) after checking `pio device list`.
- **`dialout` permission errors** — group membership needs a re-login; udev
rules above fix most FTDI/CP210x/CH340 cases.

93
docs/guides/cli/tea.md Normal file
View file

@ -0,0 +1,93 @@
# tea (Gitea/Forgejo CLI)
## What it is
`tea` is the official CLI for Gitea and Forgejo instances — repos, pull
requests, issues, releases, notifications. Since Forgejo is our default forge,
`tea` is the **primary forge CLI** on this fleet.
## Install
Debian/Ubuntu:
```bash
sudo apt install tea # may lag upstream; fine for basics
```
Latest stable binary (x86_64; use `linux-arm64` on arm64):
```bash
curl -fsSL -o ~/.local/bin/tea \
https://dl.gitea.com/tea/$(curl -fsSL https://dl.gitea.com/tea/version.json | grep -oP '"Version":\s*"\K[^"]+')/tea-linux-amd64
chmod +x ~/.local/bin/tea
```
Or build/pin via the Gitea releases page if you need a specific version.
## Authenticate
Create an access token first: in Forgejo → *Settings → Applications → Generate
New Token* (scopes: `repo`, `issue`, `read:org`). Store it in Vaultwarden
(https://vault.portugalfuturista.org).
```bash
tea login add \
--name portugalfuturista \
--url https://code.portugalfuturista.org \
--token <token-from-vaultwarden>
tea login default portugalfuturista
```
Logins live in `~/.config/tea/config.yml` (keep it mode 600 — it holds tokens).
## Configure for this environment
```bash
# inside a clone
git remote -v
# origin https://code.portugalfuturista.org/portugalfuturista/<repo>.git
```
`tea` auto-detects the login from the git remote. On the LAN the same instance
is reachable at `http://192.168.0.9:3001` — prefer the TLS name so certs and
tokens stay consistent.
```bash
tea repos ls --limit 5
tea issues ls
tea pr create --title "..." --description "..."
```
## Self-hosted equivalent
`tea` *is* the self-hosted equivalent — Forgejo at
https://code.portugalfuturista.org is the self-hosted GitHub. For public
github.com mirrors only, see [gh.md](gh.md).
## Aurélio integration
The **cli-forge** skill uses `tea` for PR/issue automation against Forgejo.
Connector registry id: `forgejo` (github.com mirrors: `github`).
## Verify
```bash
tea --version
# Version: 0.x.x ...
tea logins ls
# NAME URL DEFAULT
# portugalfuturista https://code.portugalfuturista.org true
tea whoami
# <your-forgejo-username>
```
## Troubleshooting
- **`401 Unauthorized`** — token missing the needed scope; regenerate in
Forgejo with `repo`/`issue` and re-run `tea login add` (or edit
`~/.config/tea/config.yml`).
- **Wrong login picked up**`tea` matches by remote URL; set the default
with `tea login default <name>` or pass `--login`.
- **TLS error on the LAN IP** — the cert is for `code.portugalfuturista.org`;
use the name, not `192.168.0.9:3001`, with HTTPS.
- **`unknown command`** — distro package is old; install the latest binary.

View file

@ -0,0 +1,118 @@
# terraform / tofu (Terraform + OpenTofu)
## What it is
Infrastructure-as-code CLIs. **OpenTofu** (`tofu`) is the open-source
(MPL-2.0) fork of Terraform and is our default; `terraform` is kept for modules
or providers that lag Tofu compatibility. CLIs are drop-in compatible for
current Terraform 1.5-era workflows.
## Install
OpenTofu (preferred — official apt repo):
```bash
curl -fsSL https://get.opentofu.org/install-opentofu.sh -o /tmp/install-opentofu.sh
chmod +x /tmp/install-opentofu.sh
sudo /tmp/install-opentofu.sh --install-method deb
```
Terraform (HashiCorp apt repo):
```bash
curl -fsSL https://apt.releases.hashicorp.com/gpg \
| sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com $(lsb_release -cs) main" \
| sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
```
Both publish static binaries too — pin versions via `required_version` in your
root module rather than relying on whatever is installed.
## Authenticate
Per provider, via environment variables — keep the real secrets in Vaultwarden
(https://vault.portugalfuturista.org) and load them into the shell:
```bash
export AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... # aws provider
export ARM_CLIENT_ID=... ARM_CLIENT_SECRET=... # azurerm
```
## Configure for this environment
Dev targets are the self-hosted emulators on lattepanda (`192.168.0.40`), so
provider blocks point at them:
```hcl
provider "aws" {
region = "us-east-1"
access_key = "test"
secret_key = "test"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
endpoints {
s3 = "http://192.168.0.40:4566" # LocalStack
lambda = "http://192.168.0.40:4566"
}
}
```
## Self-hosted equivalent
**State on MinIO** via the S3 backend (`backend.tf`):
```hcl
terraform {
backend "s3" {
bucket = "tf-state"
key = "lab/terraform.tfstate"
region = "us-east-1"
endpoint = "http://192.168.0.40:9000"
access_key = "<minio-access-key>"
secret_key = "<minio-secret-key>"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_region_validation = true
force_path_style = true
}
}
```
Create the bucket first (`aws --endpoint-url http://192.168.0.40:9000 s3 mb
s3://tf-state`), then `tofu init`. Better still, pass the MinIO keys via
`AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY` at init time so secrets never sit
in `.tf` files.
## Aurélio integration
The **cli-devops** skill runs `tofu plan/apply` against lab stacks. Connector
registry ids: `terraform` (tool), `minio` (state backend), `aws-localstack`
(provider target).
## Verify
```bash
tofu version
# OpenTofu v1.x.y
terraform version
# Terraform v1.x.y
tofu init && tofu validate
# Success! The configuration is valid.
```
## Troubleshooting
- **`Error acquiring the state lock`** — with the MinIO backend there's no
DynamoDB lock table; don't run two applies concurrently (MinIO conditional
writes give basic protection only).
- **Provider downloads blocked offline**`tofu init` needs registry access
once; use a filesystem mirror or vendor providers into
`~/.terraform.d/plugins`.
- **LocalStack endpoint ignored** — every `endpoints {}` service you use must
be listed; unknown services fall through to real AWS.
- **`InvalidSignatureException` on MinIO state** — clock skew or wrong keys;
sync NTP and re-check Vaultwarden entry.

View file

@ -0,0 +1,83 @@
# u-boot-tools (mkimage & friends)
## What it is
The `u-boot-tools` package provides host-side utilities for U-Boot images:
`mkimage` (wrap kernels/ramdisks/scripts into U-Boot's legacy/FIT formats),
`mkenvimage` (binary environment images), `dumpimage`, and `fw_printenv` /
`fw_setenv` (read/write the U-Boot environment from Linux).
## Install
Debian/Ubuntu:
```bash
sudo apt install u-boot-tools
```
(For device-tree work also grab `device-tree-compiler`.)
## Authenticate
None — local file manipulation tools.
## Configure for this environment
Common lab uses:
```bash
# legacy uImage for a kernel
mkimage -A arm -O linux -T kernel -C none -a 0x80008000 -e 0x80008000 \
-n "lab-kernel" -d zImage uImage
# FIT image (kernel + dtb + ramdisk) — preferred on modern boards
mkimage -f image.its fitImage.itb
# boot script
mkimage -A arm -T script -C none -n "boot script" -d boot.cmd boot.scr
# environment image
mkenvimage -s 0x20000 -o uboot.env env.txt
```
FIT source files (`.its`) and board-specific addresses live with the board
bring-up docs in the firmware repo on Forgejo — don't guess addresses, copy
them from the vendor BSP.
## Self-hosted equivalent
Fully local tooling; no cloud equivalent exists or is needed. For **OTA /
A-B updates** on deployed boards, the self-hosted stack is **SWUpdate**
(`swupdate` on the target, update artifacts served from any LAN HTTP server or
MinIO at `http://192.168.0.40:9000`) — that is our cloud-free alternative to
managed update services like Mender cloud.
## Aurélio integration
The **embedded-linux** skill uses `mkimage` when packaging kernels/ramdisks
for U-Boot boards, and mentions `swupdate` for field update flows. No
connector-registry entry — local build tooling.
## Verify
```bash
mkimage -V
# mkimage version 20xx.xx ...
mkimage -l uImage # on a real image
# Image Name: lab-kernel
# Image Type: ARM Linux Kernel Image (uncompressed)
# ...
```
## Troubleshooting
- **`mkimage: Can't open ...`** — input path wrong; remember `-d <datafile>`
is the payload, output is the last positional argument.
- **Board rejects the image (`Bad Magic Number`)** — wrong architecture `-A`
or load/entry addresses; take them from the vendor BSP, not another board.
- **FIT fails to boot with "hash mismatch"** — the `.its` referenced files
changed after `mkimage` ran, or signing keys differ; rebuild the FIT and
check `sign-images` config in U-Boot.
- **`fw_printenv` CRC error** — environment not yet saved on the board
(`saveenv` once from U-Boot), or `/etc/fw_env.config` offsets don't match
the partition layout.

77
docs/guides/cli/vercel.md Normal file
View file

@ -0,0 +1,77 @@
# vercel (Vercel CLI)
## What it is
Vercel's CLI: link a project, get instant preview deploys from a branch,
promote to production, manage env vars and domains.
## Install
Via npm (current stable; requires Node LTS):
```bash
npm install -g vercel
```
Standalone binaries also exist on the releases page if you don't want a global
npm package.
## Authenticate
```bash
vercel login # email link confirmation
```
Token cached in `~/.vercel/` (actually platform config dir). For CI, create a
token in the dashboard, store it in Vaultwarden
(https://vault.portugalfuturista.org), and use:
```bash
vercel --token "$VERCEL_TOKEN" deploy --prod
```
## Configure for this environment
```bash
cd <project>
vercel link # pick scope + project
vercel env pull .env.local # fetch env vars for local dev
vercel dev # local dev server with Vercel routing
vercel # preview deploy
vercel --prod # production
```
## Self-hosted equivalent
None deployed. The planned self-hosted alternative is **Coolify** (or Dokku)
on a Proxmox CT — git-push deploys with preview-like behaviour. Until then,
static frontends are served via Caddy on the LAN behind the `pf-tunnel-v2`
Cloudflare tunnel; Vercel is used only when its edge network or Next.js
integration is genuinely required.
## Aurélio integration
The **cli-edge** skill wraps `vercel` for preview/prod deploys of edge
frontends. Connector registry id: `vercel`.
## Verify
```bash
vercel --version
# 3x.y.z
vercel whoami
# > Success! <email> ...
vercel ls
# ... project deployments table ...
```
## Troubleshooting
- **`vercel login` email never arrives** — check spam; or use
`vercel login <email>` and the token flow from the dashboard instead.
- **Wrong scope/team**`vercel switch` to change the active team, or
re-run `vercel link`.
- **Build works locally, fails on Vercel** — Node version mismatch; pin
`"engines"` in `package.json` or the project settings.
- **Env var missing in preview** — Vercel envs are per-environment
(Production/Preview/Development); set the var for the right target.

101
docs/guides/cli/yocto.md Normal file
View file

@ -0,0 +1,101 @@
# yocto (Yocto Project / poky + kas)
## What it is
The Yocto Project builds custom Linux distributions from recipes. `poky` is
the reference distribution; `kas` is the config-driven wrapper that pins
layers/revisions in one YAML file. This is how we build appliance images for
embedded targets.
## Requirements (read before building)
- **Disk: ~100 GB+ free** (sstate cache + downloads grow fast; 200 GB is
comfortable).
- **RAM: 16 GB** (8 GB works with a big swapfile and patience).
- Long builds: first `core-image-minimal` can take hours on a laptop.
## Install (host tools)
```bash
sudo apt install gawk wget git diffstat unzip texinfo gcc build-essential \
chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils \
iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev \
pylint xterm python3-subunit mesa-common-dev zstd liblz4-tool file locales
pipx install kas
```
## Recommended: build inside the crops/poky container
The laptop should not be a Yocto host. Use the official container (Ubuntu
base, all deps preinstalled):
```bash
docker run --rm -it \
-v ~/yocto:/work -w /work \
crops/poky:ubuntu-22.04
# inside the container:
git clone -b scarthgap https://git.yoctoproject.org/poky
source poky/oe-init-build-env build
bitbake core-image-minimal
```
With kas instead of manual sourcing:
```bash
kas build project.yml # runs bitbake per the pinned layer config
kas shell project.yml # interactive shell inside the build env
```
A dedicated **Proxmox CT/VM for Yocto builds** (100+ GB disk, 16 GB RAM,
Docker or bare Debian) may come later — designs should already assume builds
run there, not on the laptop.
## Authenticate
None. Layer repos clone over HTTPS/SSH (use Forgejo mirrors where they exist:
`code.portugalfuturista.org`). Private-layer SSH keys stay in `~/.ssh`; never
in recipes.
## Configure for this environment
- Set `DL_DIR` and `SSTATE_DIR` outside the build tree so multiple builds
share downloads/cache (`local.conf`):
```
DL_DIR ?= "/work/downloads"
SSTATE_DIR ?= "/work/sstate-cache"
```
- Pin every layer branch in the kas YAML — reproducibility over freshness.
- Archive release images to MinIO (`http://192.168.0.40:9000`).
## Self-hosted equivalent
Yocto is inherently self-hosted — the entire toolchain and mirrorable source
cache live on your infrastructure. For air-gapped reproducibility run an
`sstate` mirror on MinIO (`SSTATE_MIRRORS` in `local.conf`).
## Aurélio integration
The **embedded-linux** skill drives `kas`/`bitbake` for image builds. Images
produced are flashed with tools from [esptool.md](esptool.md) /
[uboot-tools.md](uboot-tools.md) and target the `mqtt-local` connector at
runtime.
## Verify
```bash
kas --version
# kas 4.x
docker run --rm crops/poky:ubuntu-22.04 bitbake --version
# BitBake Build Tool Core version 2.x ...
```
## Troubleshooting
- **`do_fetch` stalls** — upstream git flaky; set a premirror or retry; check
you cloned the matching release branch (e.g. `scarthgap`) for all layers.
- **Disk full mid-build** — Yocto needs ~100 GB; clean with
`bitbake -c cleanall <recipe>` or prune `tmp/` (keep `DL_DIR`/`SSTATE_DIR`).
- **Host distro too new/old for a release** — that's exactly why we build in
`crops/poky`; pick the container Ubuntu version matching the Yocto release.
- **`kas` can't find layers** — paths in the kas YAML are relative to the
repo root; run `kas build` from there or pass absolute `--target` paths.

View file

@ -0,0 +1,134 @@
# Cloud emulators (self-hosted dev cloud) — MinIO + LocalStack + Azurite
Self-hosted cloud services for development, running on **lattepanda**
(192.168.0.40) as Docker containers. Part of the "self-hosted first" policy:
dev work targets these endpoints; public clouds only when explicitly required.
## What's deployed
| Service | Emulates | Endpoint (LAN) | Console |
|---|---|---|---|
| MinIO | AWS S3 | `http://192.168.0.40:9000` | `http://192.168.0.40:9001` |
| LocalStack v3 | AWS (S3, SQS, SNS, IAM, Lambda, DynamoDB, IoT, …) | `http://192.168.0.40:4566` | — |
| Azurite | Azure Storage | blob `:10000` · queue `:10001` · table `:10002` | — |
Pre-created MinIO buckets: `terraform-state`, `firmware-artifacts`, `backups`.
> **Why LocalStack v3?** `localstack/localstack:latest` (2026+) requires a
> `LOCALSTACK_AUTH_TOKEN` (Pro license). The v3 tag is the last community
> line that runs token-free.
## Deploy / manage
The stack lives at `/opt/cloud-emulators/` on lattepanda:
```
/opt/cloud-emulators/
├── cloud-emulators.yml # compose file (source of truth: repo docs/compose/cloud-emulators.yml)
└── .env # MINIO_ROOT_USER / MINIO_ROOT_PASSWORD (chmod 600)
```
```bash
ssh root@192.168.0.40
cd /opt/cloud-emulators
docker compose -f cloud-emulators.yml up -d # start/update
docker compose -f cloud-emulators.yml ps # status
docker compose -f cloud-emulators.yml logs -f # logs
```
After editing `docs/compose/cloud-emulators.yml` in the repo, sync it:
```bash
scp docs/compose/cloud-emulators.yml root@192.168.0.40:/opt/cloud-emulators/
ssh root@192.168.0.40 'cd /opt/cloud-emulators && docker compose -f cloud-emulators.yml up -d'
```
## Credentials
MinIO root credentials are in `/opt/cloud-emulators/.env` on lattepanda
(chmod 600). Store a copy in **Vaultwarden** (entry: "MinIO lattepanda").
LocalStack and Azurite accept any credentials in dev — use `test`/`test`.
## CLI wiring
### aws CLI → MinIO / LocalStack
Add to `~/.aws/config`:
```ini
[profile localstack]
region = eu-west-1
output = json
[profile minio]
region = eu-west-1
output = json
```
`~/.aws/credentials`:
```ini
[localstack]
aws_access_key_id = test
aws_secret_access_key = test
[minio]
aws_access_key_id = minioadmin
aws_secret_access_key = <from Vaultwarden>
```
Usage:
```bash
aws --profile localstack --endpoint-url http://192.168.0.40:4566 sqs list-queues
aws --profile minio --endpoint-url http://192.168.0.40:9000 s3 ls
```
(See `docs/guides/cli/aws.md` for full setup.)
### az CLI → Azurite
```bash
export AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://192.168.0.40:10000/devstoreaccount1;QueueEndpoint=http://192.168.0.40:10001/devstoreaccount1;TableEndpoint=http://192.168.0.40:10002/devstoreaccount1;"
az storage container list --connection-string "$AZURE_STORAGE_CONNECTION_STRING"
```
(The Azurite account key above is the well-known public dev key — fine for LAN dev.)
### OpenTofu/Terraform state on MinIO
```hcl
terraform {
backend "s3" {
bucket = "terraform-state"
key = "lab/terraform.tfstate"
region = "eu-west-1"
endpoint = "http://192.168.0.40:9000"
access_key = "minioadmin" # or env AWS_ACCESS_KEY_ID
secret_key = "…" # or env AWS_SECRET_ACCESS_KEY
skip_credentials_validation = true
skip_metadata_api_check = true
skip_region_validation = true
force_path_style = true
}
}
```
## Verify
```bash
curl -sf http://192.168.0.40:9000/minio/health/live # MinIO → 200
curl -sf http://192.168.0.40:4566/_localstack/health | jq # LocalStack → services available
curl -s -o /dev/null -w '%{http_code}\n' http://192.168.0.40:10000/ # Azurite → 400 (up, auth required)
```
## Troubleshooting
- **LocalStack restart loop, exit 55** — you're on `latest` (Pro-only). The
compose file pins `localstack/localstack:3`; don't bump without a license.
- **MinIO `mc` commands** — exec inside the container:
`docker exec minio mc ls local` (alias `local` preconfigured at bootstrap).
- **Azurite 400 on root URL** — expected; the service requires an account path.
Use the connection string above.
- **Disk pressure** — all three volumes live on lattepanda's root disk (85G
free at deploy time). `docker system df` to audit.