Skip to content

Commit 904d523

Browse files
committed
feat: add deployment skill for pb-mapper-client-cli with systemd service management
1 parent 40a120a commit 904d523

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
---
2+
name: pb-mapper-client-cli-deploy
3+
description: Deploy or upgrade `pb-mapper-client-cli` on a remote Linux host through SSH, install it as a managed `systemd` tunnel service, and verify the exposed API path end-to-end. Use when users ask to operationalize a client tunnel for a service key (download locally, upload remotely, run as service, and health-check).
4+
---
5+
6+
# Pb Mapper Client Cli Deploy
7+
8+
## Overview
9+
10+
Use this workflow to perform reproducible remote deployment of `pb-mapper-client-cli` with minimal manual edits.
11+
Execute from the operator machine that has SSH access to the target host.
12+
13+
## Required Inputs
14+
15+
Collect these values first:
16+
17+
- `SSH_TARGET` (example: `ubuntu@sfapi.duckdns.org`)
18+
- `VERSION` without `v` prefix (example: `0.2.7`)
19+
- `TARGET_TRIPLE` (example: `x86_64-unknown-linux-musl`)
20+
- `PB_SERVER` (example: `sfapi.duckdns.org:7666`)
21+
- `SERVICE_KEY` (example: `sf-backend`)
22+
- `LOCAL_ADDR` to expose on remote host (example: `127.0.0.1:39080`)
23+
- `PUBLIC_CHECK_URL` for external validation (example: `https://sfapi.duckdns.org/api/semantic-search?q=test`)
24+
- optional `MSG_HEADER_KEY` (exactly 32 chars). Leave empty to use default key behavior.
25+
26+
## Deployment Workflow
27+
28+
### 1. Prepare local artifact
29+
30+
Use deterministic file names:
31+
32+
```bash
33+
export SSH_TARGET="ubuntu@sfapi.duckdns.org"
34+
export VERSION="0.2.7"
35+
export TARGET_TRIPLE="x86_64-unknown-linux-musl"
36+
export PB_SERVER="sfapi.duckdns.org:7666"
37+
export SERVICE_KEY="sf-backend"
38+
export LOCAL_ADDR="127.0.0.1:39080"
39+
export PUBLIC_CHECK_URL="https://sfapi.duckdns.org/api/semantic-search?q=test"
40+
# Optional. Keep empty to use default header key behavior.
41+
export MSG_HEADER_KEY="OMbmiQpCTVKqEMzVUzm8YQgmbQR1bbn0"
42+
43+
export ASSET_FILE="pb-mapper-client-cli-v${VERSION}-${TARGET_TRIPLE}.tar.gz"
44+
export ASSET_URL="https://github.com/acking-you/pb-mapper/releases/download/v${VERSION}/${ASSET_FILE}"
45+
46+
curl -fL "${ASSET_URL}" -o "/tmp/${ASSET_FILE}"
47+
test -s "/tmp/${ASSET_FILE}"
48+
```
49+
50+
### 2. Upload and install binary on remote host
51+
52+
```bash
53+
scp "/tmp/${ASSET_FILE}" "${SSH_TARGET}:/tmp/${ASSET_FILE}"
54+
55+
ssh "${SSH_TARGET}" "ASSET_FILE='${ASSET_FILE}' sudo bash -s" <<'REMOTE_INSTALL'
56+
set -euo pipefail
57+
TMP_DIR="/tmp/pb-mapper-client-cli-install"
58+
INSTALL_DIR="/opt/pb-mapper-client-cli/current"
59+
60+
rm -rf "${TMP_DIR}"
61+
mkdir -p "${TMP_DIR}"
62+
tar -xzf "/tmp/${ASSET_FILE}" -C "${TMP_DIR}"
63+
64+
BIN_PATH="$(find "${TMP_DIR}" -type f -name pb-mapper-client-cli | head -n 1)"
65+
if [ -z "${BIN_PATH}" ]; then
66+
echo "pb-mapper-client-cli binary not found in archive" >&2
67+
exit 1
68+
fi
69+
70+
sudo install -d "${INSTALL_DIR}"
71+
sudo install -m 0755 "${BIN_PATH}" "${INSTALL_DIR}/pb-mapper-client-cli"
72+
sudo rm -rf "${TMP_DIR}" "/tmp/${ASSET_FILE}"
73+
REMOTE_INSTALL
74+
```
75+
76+
### 3. Create or update systemd unit
77+
78+
Use a templated service so multiple tunnel instances can coexist:
79+
80+
```bash
81+
ssh "${SSH_TARGET}" "sudo bash -s" <<'REMOTE_SYSTEMD'
82+
set -euo pipefail
83+
SERVICE_TEMPLATE="/etc/systemd/system/pb-mapper-client-cli@.service"
84+
sudo tee "${SERVICE_TEMPLATE}" >/dev/null <<'UNIT'
85+
[Unit]
86+
Description=pb-mapper client tunnel (%i)
87+
After=network-online.target
88+
Wants=network-online.target
89+
90+
[Service]
91+
Type=simple
92+
EnvironmentFile=/etc/pb-mapper/client-cli/%i.env
93+
ExecStart=/opt/pb-mapper-client-cli/current/pb-mapper-client-cli \
94+
--pb-mapper-server ${PB_SERVER} \
95+
tcp-server \
96+
--key ${SERVICE_KEY} \
97+
--addr ${LOCAL_ADDR}
98+
Restart=always
99+
RestartSec=2
100+
101+
[Install]
102+
WantedBy=multi-user.target
103+
UNIT
104+
105+
sudo install -d /etc/pb-mapper/client-cli
106+
REMOTE_SYSTEMD
107+
```
108+
109+
### 4. Write instance env file and start service
110+
111+
`MSG_HEADER_KEY` must be omitted when empty; never write an empty value to env file.
112+
113+
```bash
114+
export INSTANCE_NAME="${SERVICE_KEY}"
115+
116+
ssh "${SSH_TARGET}" \
117+
"INSTANCE_NAME='${INSTANCE_NAME}' PB_SERVER='${PB_SERVER}' SERVICE_KEY='${SERVICE_KEY}' LOCAL_ADDR='${LOCAL_ADDR}' MSG_HEADER_KEY='${MSG_HEADER_KEY}' sudo bash -s" <<'REMOTE_ENV'
118+
set -euo pipefail
119+
120+
ENV_FILE="/etc/pb-mapper/client-cli/${INSTANCE_NAME}.env"
121+
sudo tee "${ENV_FILE}" >/dev/null <<EOF
122+
PB_SERVER=${PB_SERVER}
123+
SERVICE_KEY=${SERVICE_KEY}
124+
LOCAL_ADDR=${LOCAL_ADDR}
125+
RUST_LOG=info
126+
PB_MAPPER_KEEP_ALIVE=ON
127+
EOF
128+
129+
if [ -n "${MSG_HEADER_KEY}" ]; then
130+
CLEAN_KEY="$(printf '%s' "${MSG_HEADER_KEY}" | tr -d '\r\n')"
131+
if [ "${#CLEAN_KEY}" -ne 32 ]; then
132+
echo "MSG_HEADER_KEY must be exactly 32 characters" >&2
133+
exit 1
134+
fi
135+
echo "MSG_HEADER_KEY=${CLEAN_KEY}" | sudo tee -a "${ENV_FILE}" >/dev/null
136+
fi
137+
138+
sudo systemctl daemon-reload
139+
sudo systemctl enable --now "pb-mapper-client-cli@${INSTANCE_NAME}.service"
140+
REMOTE_ENV
141+
```
142+
143+
### 5. Validate tunnel and external path
144+
145+
Run all checks:
146+
147+
```bash
148+
ssh "${SSH_TARGET}" "sudo systemctl --no-pager --full status pb-mapper-client-cli@${INSTANCE_NAME}.service"
149+
ssh "${SSH_TARGET}" "curl -fsS 'http://${LOCAL_ADDR}/api/semantic-search?q=test' | jq -e 'type==\"object\" or type==\"array\"' >/dev/null"
150+
curl -fsS "${PUBLIC_CHECK_URL}" | jq -e 'type==\"object\" or type==\"array\"' >/dev/null
151+
```
152+
153+
If `jq` is unavailable, print raw body and verify it is valid JSON manually.
154+
155+
## Troubleshooting Checklist
156+
157+
Use this quick triage when startup or forwarding fails:
158+
159+
- `datalen not valid`: likely `MSG_HEADER_KEY` mismatch or hidden newline; verify both sides use the same 32-byte key.
160+
- Service restarts immediately: inspect logs with `journalctl -u pb-mapper-client-cli@<instance> -n 200 --no-pager`.
161+
- Remote port not listening: confirm `LOCAL_ADDR` host/port and no port conflict.
162+
- Public URL fails but localhost works: investigate reverse proxy (for example, Caddy route/TLS config).
163+
164+
## Safe Update Procedure
165+
166+
Upgrade in place without changing the unit:
167+
168+
1. Download/upload new `vX.Y.Z` artifact.
169+
2. Re-run install step to overwrite `/opt/pb-mapper-client-cli/current/pb-mapper-client-cli`.
170+
3. Restart instance: `sudo systemctl restart pb-mapper-client-cli@<instance>.service`.
171+
4. Re-run validation checks.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
interface:
2+
display_name: "Pb Mapper Client CLI Deploy"
3+
short_description: "Deploy pb-mapper client-cli as a Linux systemd tunnel service"
4+
default_prompt: "Use this skill to deploy or upgrade pb-mapper-client-cli on a remote Linux host via SSH, manage it with systemd, and verify API tunnel health."

0 commit comments

Comments
 (0)