Skip to content

Deutsch

Install manually on one gateway

For a single gateway: integrators, tests, gateways already in the field. The agent is installed, updated and removed with the commands on this page, all run on the gateway as root. You need no other computer, no installer script and no package manager. Every file is checked before anything from it runs. For gateways built in series, put the agent into your image instead, see Install with Yocto and RAUC.

The steps were run word for word on a lab gateway (armv7, read-only root file system, /etc an overlay on the data partition /mnt/data) on 2026-11-11: an install of cproto-v0.2.0-rc.1, the update to cproto-v0.2.0-rc.2 and a reboot. What was not tested says so.

Requirements

  • Architecture: armv7 only (32-bit ARM, hard-float). There is no build for any other architecture yet.
  • glibc 2.34 or newer on the gateway: the highest symbol version the program needs is GLIBC_2.34.
  • These libraries on the gateway, as on Install with Yocto and RAUC: libtss2-esys.so.0, libtss2-mu.so.0, libcurl.so.4, libssl.so.3, libcrypto.so.3, libjson-c.so.5 and glibc.
  • The tools curl, sha256sum, tar and the openssl command, version 3, on the gateway. Check with openssl version. OpenSSL 1.1.1 and LibreSSL 3.3 stop at the signature check with unknown option -rawin; the lab gateway has OpenSSL 3.2.2. Without openssl 3 on the gateway, the only way is to check the files on another computer as in Check the signature and copy them over; this page does not cover that.
  • A data partition that stays across reboots and firmware updates, here /mnt/data, and a writable /etc (on the lab gateway an overlay on that partition).
  • The device registered in the console, with a template assigned.

1. Choose the version and the place

On the gateway, as root:

V=cproto-v0.2.0-rc.2
D=/mnt/data/bootstrap-agent
A=bootstrap-agent-$V-linux-armv7.tar.gz
U=https://packages.pxdc.io/bootstrap-agent/$V

D is where the agent lives on the data partition; use your own data partition. Every version gets its own folder under D; D/current points to the one that runs. Keep the shell open: the next steps use these variables.

2. Save the key, once

The public key of the agent's releases. Save it once; it is the same for every version.

mkdir -p /etc/bootstrap-agent
cat > /etc/bootstrap-agent/wlpc-agent-ed25519.pub <<'EOF'
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAN8+mCK4/qSV4xO6uFNHrc5+8Dje5wK609NI7WEGqYa4=
-----END PUBLIC KEY-----
EOF
openssl pkey -pubin -in /etc/bootstrap-agent/wlpc-agent-ed25519.pub -outform DER | sha256sum

The last command must print 79c12a4d7b4cc3868129a0afac587fd2441246e5c61382bd8221bc2ec52d402a. Compare it also with the fingerprint support sends you separately: a key copied from the same place as the archive proves nothing by itself.

3. Download and check

mkdir -p $D/$V && cd $D/$V
curl -fsSLO $U/$A
curl -fsSLO $U/$A.sig
curl -fsSLO $U/$A.sha256
sha256sum -c $A.sha256
openssl pkeyutl -verify -pubin -rawin -inkey /etc/bootstrap-agent/wlpc-agent-ed25519.pub -sigfile $A.sig -in $A

Go on only after OK and Signature Verified Successfully. On the lab gateway a copy with one byte appended was refused with Signature Verification Failure. Then unpack and check every file inside:

mkdir files && tar -xzf $A -C files
chown -R root:root files && chmod 0755 files
(cd files && sha256sum -c CHECKSUMS.sha256)

Every line must end in OK. The chown/chmod line is for cproto-v0.2.0-rc.1: that archive records its top folder with mode 0700, so files would get that mode and the agent's user could not run the program (status=203/EXEC). For later versions the line changes nothing.

cproto-v0.2.0-rc.1: no confirmation from the stand-in

In that archive the example service in examples/ does not write the confirmation file at all, and the tmpfiles rule makes /run/wlpc writable for root only. With cproto-v0.2.0-rc.1 the device therefore stays PROVISIONED (measured). From cproto-v0.2.0-rc.2 on, the stand-in in step 7 writes the file as a member of the group wlpc.

4. User and groups

The agent runs as the user bootstrap-agent; it needs the group tss for the TPM, and your software writes the confirmation as a member of the group wlpc:

grep -E '^(tss|wlpc):' /etc/group
grep -q '^wlpc:' /etc/group || addgroup -S wlpc
id bootstrap-agent || { addgroup -S bootstrap-agent && adduser -S -D -H -h /nonexistent -s /bin/false -G bootstrap-agent bootstrap-agent; }

The last line creates the user only if it is missing: without a home directory, without a login shell, in its own group, as the image recipe does (groupadd/useradd on a system without BusyBox). The group tss comes with the TPM software of the firmware; if the first line does not show it, create it with addgroup -S tss.

Partly run on the lab gateway

On the lab gateway the user and the groups tss and wlpc existed, so the lines ran but created nothing. The creating branch was not run.

5. Install

ln -sfn $D/$V/files $D/current
install -m 0644 $D/current/systemd/bootstrap-agent.service /etc/systemd/system/
install -m 0644 $D/current/tmpfiles/bootstrap-agent.conf /etc/tmpfiles.d/
mkdir -p /etc/systemd/system/bootstrap-agent.service.d
cat > /etc/systemd/system/bootstrap-agent.service.d/manual.conf <<EOF
[Service]
ExecStart=
ExecStart=$D/current/bootstrap-agent
EnvironmentFile=
EnvironmentFile=$D/current/env/bootstrap-agent.env
EnvironmentFile=-/etc/bootstrap-agent/agent.env
EOF
printf 'BOOTSTRAP_AGENT_URL=https://bootstrap.ztd.dev.pxdc.io\nBOOTSTRAP_AGENT_TRUST_BUNDLE=\n' > /etc/bootstrap-agent/agent.env
  • The unit expects the program in /usr/bin and its default settings in /usr/lib/wlpc, both on the read-only root. The drop-in manual.conf points them to D/current. It is the only difference from an image install.
  • agent.env sets the platform address, see Settings. The trust bundle is set empty: the default points to a file that only an image provides.
  • The archive's tmpfiles rule creates /run/wlpc with mode 2775 for the group wlpc, see Who may write /run/wlpc.

6. Start

systemd-tmpfiles --create /etc/tmpfiles.d/bootstrap-agent.conf
systemctl daemon-reload
$D/current/bootstrap-agent --version
systemctl enable --now bootstrap-agent
journalctl -u bootstrap-agent -n 30 --no-pager

--version prints the version you set (cproto-v0.2.0-rc.1 in the test). On the lab gateway it also printed /usr/lib/libcurl.so.4: no version information available; that warning is harmless (the gateway's curl library carries no symbol versions). The log then shows, within about three seconds: local interface listening, attestation key loaded, profile persisted and served, profile-ready marker written and bootstrap complete reported to the platform. The console shows the device as PROVISIONED. Check the agent with:

curl -s --unix-socket /run/bootstrap-agent/api.sock http://localhost/v1/status

"state":"ready" and a profile_version mean the configuration is stored.

7. Your software, or a stand-in

Your software starts when the configuration is there and writes the confirmation, see Start your software and confirm. Without it, use the examples from the archive as a stand-in:

cp $D/current/examples/profile-ready-consumer.path /etc/systemd/system/your-software.path
cp $D/current/examples/profile-ready-consumer.service /etc/systemd/system/your-software.service
sed -i 's/^Unit=.*/Unit=your-software.service/' /etc/systemd/system/your-software.path
systemctl daemon-reload
systemctl enable --now your-software.path
systemctl restart your-software.service

The sed line points the .path unit at the renamed service: the example names profile-ready-consumer.service, and without the line systemd refuses to start the .path unit (unit profile-ready-consumer.service to trigger not loaded).

The service waits for the agent's socket, makes one call, writes {"state":"connected"} to /run/wlpc/target-confirmed under its own user ID (as a member of wlpc, no root) and ends; it stays active (RemainAfterExit=yes), so the .path unit does not start it again in a loop.

On the lab gateway, with cproto-v0.2.0-rc.2, the stand-in wrote the file under its own user ID (group wlpc, mode 0644, content {"state":"connected"}). Check with ls -ln /run/wlpc and systemctl is-active your-software.path your-software.service (both active). The stand-in writes the file at once; your software writes it only when it is connected, and again after every new configuration, see Write the file again for every new configuration.

8. Update to a new version

Set the new version, then repeat steps 3, 5, 6 and 7 with it; step 2 is not needed again. Step 7 matters: the stand-in is copied from the archive, and the one from cproto-v0.2.0-rc.1 does not write the confirmation at all. For example:

V=cproto-v0.2.0-rc.2
A=bootstrap-agent-$V-linux-armv7.tar.gz
U=https://packages.pxdc.io/bootstrap-agent/$V

Step 5 points D/current to the new version and installs its unit and tmpfiles rule; step 6 restarts nothing by itself, so restart the agent:

systemctl restart bootstrap-agent

The configuration in /etc/iot-config stays. The old version's folder stays too; to go back, set V to the old version and run steps 5 and 6 again. Remove an old folder with rm -rf $D/OLD-VERSION once you no longer need it.

On the lab gateway the update from cproto-v0.2.0-rc.1 to cproto-v0.2.0-rc.2 checked out like the install (OK, Signature Verified Successfully). The new agent served the stored configuration at once (serving the profile already on this device; scheduling one boot check-in), fetched nothing new (check-in complete: unchanged, nothing fetched), and the device went to ACTIVE once the confirmation file was there.

9. Firmware updates and reboots

Reboot: after a reboot of the lab gateway the agent started on its own, served the stored configuration and checked in (restored profile is fully acked; its confirmation rides on the next check-in, then pending confirmation delivered on check-in and check-in complete: unchanged). The device stayed ACTIVE; a repeated confirmation is not billed again.

The stand-in after the reboot

In the test the .path unit did not start after the reboot, because it had been installed without the sed line in step 7 (unit profile-ready-consumer.service to trigger not loaded). With the line it started and wrote the file; a second reboot with the corrected unit was not run.

Firmware update: everything this page installs is on the data partition (D) or in /etc, which on the lab gateway is an overlay on that partition. A firmware update that replaces the root file system and keeps the data partition therefore keeps the agent installed, as long as the new firmware still has the libraries and tools from the requirements and mounts /etc the same way. A factory reset that clears the data partition removes it; run bootstrap-agent --release-ak first, see The TPM.

Not tested

No firmware update and no factory reset were run on the lab gateway with this install.

10. Remove it

How to remove the agent, what stays behind and how to install it again: Uninstall.

What differs from production

  • Check-in interval. The agent's default is one hour (range 300 to 86400 seconds, see Device actions). The lab gateway calls in every 5 minutes because its template sets check_in_interval_seconds to 300. To see a template change at the gateway sooner, use that key; keep the default in production.
  • Series. Installing by hand on each gateway does not scale and is not covered by a firmware update you ship. For series, use Install with Yocto and RAUC.