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.5and glibc. - The tools
curl,sha256sum,tarand theopensslcommand, version 3, on the gateway. Check withopenssl version. OpenSSL 1.1.1 and LibreSSL 3.3 stop at the signature check withunknown option -rawin; the lab gateway has OpenSSL 3.2.2. Withoutopenssl3 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/binand its default settings in/usr/lib/wlpc, both on the read-only root. The drop-inmanual.confpoints them toD/current. It is the only difference from an image install. agent.envsets 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/wlpcwith mode 2775 for the groupwlpc, 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:
"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:
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_secondsto 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.