🧪 Skills

Seed

Grow firmware on any hardware through HTTP — upload C, compile on device, apply with watchdog rollback

v0.1.2
❤️ 0
⬇️ 61
👁 1
Share

Description


name: seed description: Grow firmware on any hardware through HTTP — upload C, compile on device, apply with watchdog rollback homepage: https://github.com/Awis13/seed user-invocable: true metadata: {"openclaw":{"emoji":"🌱","os":["darwin","linux"],"requires":{"bins":["curl","gcc"]}}}

seed.c — Self-Growing Firmware

Source: https://github.com/Awis13/seed

You have access to a seed node — a minimal C firmware with an HTTP API that you can grow by writing and compiling new firmware on the device.

First steps

If the user hasn't given you a node address and token, ask them:

  • Do they already have a seed node running? If so, get the IP and token.
  • If not, offer to deploy one. All they need is a Linux machine with gcc:
# On the target machine (Pi, VPS, any Linux box):
curl -fsSL https://raw.githubusercontent.com/Awis13/seed/main/seeds/linux/seed.c -o seed.c
gcc -O2 -o seed seed.c
./seed 8080
# Token will be printed — give it to the agent

The seed binary is ~70KB, zero dependencies beyond libc.

Connecting

The node prints its address and token on startup. If you don't know them:

# The user will provide the address, or:
curl http://<ip>:8080/health          # no auth needed
curl http://<ip>:8080/skill           # full connection details + token

All requests except /health need: Authorization: Bearer <token>

What you can do

1. Discover the hardware

curl -H "Authorization: Bearer $TOKEN" http://$HOST/capabilities

Returns: arch, CPU, RAM, disk, temperature, GPIO, I2C, serial ports, USB devices, WiFi, Bluetooth, WireGuard — everything the node has.

2. Read the running firmware

curl -H "Authorization: Bearer $TOKEN" http://$HOST/firmware/source

Returns the C source code currently running on the node.

3. Write new firmware

curl -H "Authorization: Bearer $TOKEN" \
  -X POST --data-binary @new_firmware.c \
  http://$HOST/firmware/source

Upload C source code. The node saves it for compilation.

4. Compile on the device

curl -H "Authorization: Bearer $TOKEN" -X POST http://$HOST/firmware/build

The node runs gcc -O2 on itself. Check errors with GET /firmware/build/logs.

5. Apply (hot-swap)

curl -H "Authorization: Bearer $TOKEN" -X POST http://$HOST/firmware/apply

Atomic binary swap. 10-second watchdog — if the new firmware fails the health check, the old version is restored automatically.

API reference

Method Path Description
GET /health Alive check (no auth)
GET /capabilities Hardware fingerprint
GET /config.md Node config (markdown)
POST /config.md Update config
GET /events Event log (?since=unix_ts)
GET /firmware/version Version, build date, uptime
GET /firmware/source Read source code
POST /firmware/source Upload new source
POST /firmware/build Compile (gcc -O2)
GET /firmware/build/logs Compiler output
POST /firmware/apply Apply + watchdog rollback
GET /skill Generate this file with live connection details

Writing firmware

Constraints:

  • C only, libc only — no external libraries
  • gcc -O2 -o seed seed.c must compile with no extra flags
  • Single-threaded, one request at a time
  • Max request body: 64KB
  • 3 failed applies -> /firmware/apply locks (POST /firmware/apply/reset to unlock)

Handler pattern:

if (strcmp(req.path, "/myendpoint") == 0
    && strcmp(req.method, "GET") == 0) {
    char resp[4096];
    snprintf(resp, sizeof(resp), "{\"key\":\"value\"}");
    json_resp(fd, 200, "OK", resp);
    goto done;
}

Available functions:

  • json_resp(fd, code, status, json) — send JSON response
  • text_resp(fd, code, status, text) — send plain text
  • respond(fd, code, status, content_type, body, len) — send anything
  • file_read(path, &len) — read file, returns malloc'd buffer
  • file_write(path, data, len) — write file
  • cmd_out(shell_cmd, buf, bufsize) — run command, capture output
  • event_add(fmt, ...) — log event

Request struct: req.method, req.path, req.body (malloc'd, may be NULL), req.body_len, req.content_length. Path includes query string.

Auth is already checked before your handler. /health is always public.

Capabilities example

{
  "type": "seed", "version": "0.1.0", "seed": true,
  "hostname": "raspberrypi", "arch": "armv6l",
  "cpus": 1, "mem_mb": 512, "disk_mb": 14000,
  "temp_c": 42.3, "board_model": "Raspberry Pi Zero W Rev 1.1",
  "has_gcc": true,
  "net_interfaces": ["eth0", "wlan0", "usb0"],
  "serial_ports": ["/dev/ttyAMA0"],
  "i2c_buses": ["/dev/i2c-1"],
  "gpio_chips": ["/dev/gpiochip0"],
  "has_wifi": true, "has_bluetooth": true,
  "endpoints": ["/health", "/capabilities", "/config.md", ...]
}

Use this to decide what the node can do and what firmware to write.

Typical workflow

  1. GET /health — is it alive?
  2. GET /capabilities — what hardware does it have?
  3. GET /firmware/source — read the current code
  4. Write new firmware that adds the endpoints you need
  5. POST /firmware/source — upload it
  6. POST /firmware/build — compile
  7. GET /firmware/build/logs — check for errors
  8. POST /firmware/apply — deploy (auto-rollback on failure)
  9. Test the new endpoints
  10. Repeat — the grown firmware still has /firmware/*, so you can grow it again

Reviews (0)

Sign in to write a review.

No reviews yet. Be the first to review!

Comments (0)

Sign in to join the discussion.

No comments yet. Be the first to share your thoughts!

Compatible Platforms

Pricing

Free

Related Configs