docs(guides): CLI install guides — forge/cloud/devops/edge/iot/embedded-linux (24 guides + index, self-hosted-first policy)
This commit is contained in:
parent
912cf45b51
commit
8e5ba6ba93
26 changed files with 2357 additions and 0 deletions
96
docs/guides/README.md
Normal file
96
docs/guides/README.md
Normal 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 202–220), cloudflared `pf-tunnel-v2` |
|
||||||
|
| gigabyte | 192.168.0.104 | Proxmox (CTs 200–211), 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
82
docs/guides/cli/acli.md
Normal 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.
|
||||||
89
docs/guides/cli/ansible.md
Normal file
89
docs/guides/cli/ansible.md
Normal 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`.
|
||||||
83
docs/guides/cli/arduino-cli.md
Normal file
83
docs/guides/cli/arduino-cli.md
Normal 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
106
docs/guides/cli/aws.md
Normal 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
90
docs/guides/cli/az.md
Normal 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>`.
|
||||||
79
docs/guides/cli/buildroot.md
Normal file
79
docs/guides/cli/buildroot.md
Normal 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.
|
||||||
97
docs/guides/cli/cloudflared-wrangler.md
Normal file
97
docs/guides/cli/cloudflared-wrangler.md
Normal 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.
|
||||||
95
docs/guides/cli/cross-toolchains.md
Normal file
95
docs/guides/cli/cross-toolchains.md
Normal 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.
|
||||||
95
docs/guides/cli/docker-compose.md
Normal file
95
docs/guides/cli/docker-compose.md
Normal 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
80
docs/guides/cli/doctl.md
Normal 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.
|
||||||
75
docs/guides/cli/esptool.md
Normal file
75
docs/guides/cli/esptool.md
Normal 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
76
docs/guides/cli/flyctl.md
Normal 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
91
docs/guides/cli/gcloud.md
Normal 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
96
docs/guides/cli/gh.md
Normal 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
77
docs/guides/cli/helm.md
Normal 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.
|
||||||
86
docs/guides/cli/kubectl.md
Normal file
86
docs/guides/cli/kubectl.md
Normal 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.
|
||||||
94
docs/guides/cli/mosquitto-clients.md
Normal file
94
docs/guides/cli/mosquitto-clients.md
Normal 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.
|
||||||
75
docs/guides/cli/netlify.md
Normal file
75
docs/guides/cli/netlify.md
Normal 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.
|
||||||
89
docs/guides/cli/platformio.md
Normal file
89
docs/guides/cli/platformio.md
Normal 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
93
docs/guides/cli/tea.md
Normal 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.
|
||||||
118
docs/guides/cli/terraform-opentofu.md
Normal file
118
docs/guides/cli/terraform-opentofu.md
Normal 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.
|
||||||
83
docs/guides/cli/uboot-tools.md
Normal file
83
docs/guides/cli/uboot-tools.md
Normal 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
77
docs/guides/cli/vercel.md
Normal 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
101
docs/guides/cli/yocto.md
Normal 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.
|
||||||
134
docs/guides/selfhosted/cloud-emulators.md
Normal file
134
docs/guides/selfhosted/cloud-emulators.md
Normal 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.
|
||||||
Loading…
Reference in a new issue