Skip to content

duckctl: scp, with :path for the robot - #269

Merged
pierre-rouanet merged 1 commit into
mainfrom
duckctl-scp
Sep 10, 2026
Merged

duckctl: scp, with :path for the robot#269
pierre-rouanet merged 1 commit into
mainfrom
duckctl-scp

Conversation

@pierre-rouanet

Copy link
Copy Markdown
Member

duckctl ssh finds the robot and hands the terminal to ssh. Copying a file still meant
scp radxa@$(duckctl ip):/var/log/robotd.log . — the substitution ssh exists to remove.

duckctl scp report.md :/tmp/
duckctl scp :/var/log/robotd.log .

A path starting with : is on the robot: scp's own host:path with the host left out, because
the host is what this finds for you. The rewrite is textual and only ever looks at the first
character, so -r, -P 2222, several sources, a bare : for the home directory and a path with a
colon in the middle of it all reach scp exactly as typed — scp decides what they mean.

That is why it is one command rather than a push and a pull: both directions, under the name
somebody reaching for scp would actually type.

A copy with no : anywhere in it is refused before the scan. scp would perform it — locally,
happily — and nothing in the output would say the robot was never involved; refusing it after the
scan would charge eight seconds for the diagnosis too.

The account resolves the way ssh's does: --user, else DUCK_BOARD_USER, else radxa. The
exec that makes ssh become ssh is now become_program, shared by both, so the progress meter,
the key prompts and the exit status are scp's own.

Checked

Both directions against olducky, over a real BLE resolve:

scp /tmp/probe.txt pierre@192.168.1.164:/tmp/duckctl-probe.txt
scp pierre@192.168.1.164:/tmp/duckctl-probe.txt /tmp/back.txt

and the file came back with its contents. duckctl scp a b refuses in 0.4 s with no scan. Three
new unit tests cover the rewrite, the two refusals and the parse; cargo test -p duckctl is 49
green, clippy and fmt clean.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown

Coverage

71.73% lines on this branch, against a floor of 70%.

Per-file
Filename                              Regions    Missed Regions     Cover   Functions  Missed Functions  Executed       Lines      Missed Lines     Cover    Branches   Missed Branches     Cover
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
btd/src/adv.rs                             79                 0   100.00%           9                 0   100.00%          42                 0   100.00%           0                 0         -
btd/src/bluez.rs                          482               482     0.00%          34                34     0.00%         314               314     0.00%           0                 0         -
btd/src/chorale.rs                        346               246    28.90%          23                13    43.48%         230               171    25.65%           0                 0         -
btd/src/framing.rs                        253                 9    96.44%          20                 1    95.00%         120                 5    95.83%           0                 0         -
btd/src/gatt.rs                             3                 0   100.00%           1                 0   100.00%           3                 0   100.00%           0                 0         -
btd/src/link.rs                            24                 0   100.00%           3                 0   100.00%          26                 0   100.00%           0                 0         -
btd/src/main.rs                            77                77     0.00%          11                11     0.00%          77                77     0.00%           0                 0         -
btd/src/pairing.rs                        149                 2    98.66%          12                 1    91.67%          81                 1    98.77%           0                 0         -
btd/src/route.rs                          348                32    90.80%          27                 0   100.00%         347                11    96.83%           0                 0         -
btd/src/session.rs                       1218                73    94.01%          71                 5    92.96%         706                41    94.19%           0                 0         -
btd/src/upstream.rs                       397                35    91.18%          34                 8    76.47%         241                19    92.12%           0                 0         -
configd/src/bluez.rs                      586               498    15.02%          85                79     7.06%         375               326    13.07%           0                 0         -
configd/src/identity.rs                   111                 5    95.50%          12                 1    91.67%          52                 3    94.23%           0                 0         -
configd/src/logs.rs                       314                33    89.49%          31                 3    90.32%         219                36    83.56%           0                 0         -
configd/src/main.rs                       515               515     0.00%          38                38     0.00%         345               345     0.00%           0                 0         -
configd/src/net.rs                        246                12    95.12%          38                 2    94.74%         181                 4    97.79%           0                 0         -
configd/src/nm.rs                         271               213    21.40%          43                39     9.30%         171               120    29.82%           0                 0         -
configd/src/pad.rs                        379                13    96.57%          38                 2    94.74%         222                 8    96.40%           0                 0         -
configd/src/power.rs                       30                30     0.00%           6                 6     0.00%          28                28     0.00%           0                 0         -
configd/src/store.rs                      350                25    92.86%          28                 2    92.86%         173                 9    94.80%           0                 0         -
configd/src/units.rs                      183               116    36.61%          24                18    25.00%         116                79    31.90%           0                 0         -
duck-control/src/bus.rs                   587               452    23.00%          52                37    28.85%         394               307    22.08%           0                 0         -
duck-control/src/fall.rs                  173                 0   100.00%          15                 0   100.00%         108                 0   100.00%           0                 0         -
duck-control/src/imu.rs                   370                 8    97.84%          20                 0   100.00%         195                 5    97.44%           0                 0         -
duck-control/src/io.rs                    134                19    85.82%          21                 5    76.19%         120                17    85.83%           0                 0         -
duck-control/src/model.rs                 120                 1    99.17%          15                 0   100.00%          73                 0   100.00%           0                 0         -
duck-control/src/obs.rs                   263                 6    97.72%          24                 2    91.67%         177                 8    95.48%           0                 0         -
duck-control/src/policy.rs                701               560    20.11%          67                47    29.85%         452               347    23.23%           0                 0         -
duck-control/src/safety.rs                450                13    97.11%          33                 2    93.94%         292                 6    97.95%           0                 0         -
duck-control/src/sim.rs                   426                63    85.21%          32                10    68.75%         233                40    82.83%           0                 0         -
duck-detect/src/bin/duck-bench.rs         306               306     0.00%          16                16     0.00%         170               170     0.00%           0                 0         -
duck-detect/src/lib.rs                    641                15    97.66%          27                 0   100.00%         333                 8    97.60%           0                 0         -
duck-detect/src/onnx.rs                    96                96     0.00%           5                 5     0.00%          52                52     0.00%           0                 0         -
duck-detect/src/rknn.rs                   267               237    11.24%          15                11    26.67%         216               195     9.72%           0                 0         -
duck-ether/src/main.rs                    504               404    19.84%          27                16    40.74%         270               217    19.63%           0                 0         -
duck-ipc-proto/src/lib.rs                2074               163    92.14%         133                13    90.23%        1519                88    94.21%           0                 0         -
duckctl/src/main.rs                      2617              1023    60.91%         156                45    71.15%        1629               654    59.85%           0                 0         -
kinematics/src/hand.rs                    394                 2    99.49%          22                 2    90.91%         181                 2    98.90%           0                 0         -
kinematics/src/head.rs                    392                 9    97.70%          22                 0   100.00%         193                 4    97.93%           0                 0         -
kinematics/src/lib.rs                     348                10    97.13%          23                 1    95.65%         174                 7    95.98%           0                 0         -
kinematics/src/math.rs                    171                 0   100.00%          16                 0   100.00%          79                 0   100.00%           0                 0         -
kinematics/src/mjcf.rs                    233                32    86.27%          17                 1    94.12%         134                15    88.81%           0                 0         -
kinematics/src/tof.rs                     338                17    94.97%          14                 0   100.00%         184                10    94.57%           0                 0         -
mediad/src/camera.rs                      291                 5    98.28%          21                 0   100.00%         204                 5    97.55%           0                 0         -
mediad/src/config.rs                       87                 5    94.25%           7                 1    85.71%          46                 4    91.30%           0                 0         -
mediad/src/detect.rs                      293               222    24.23%          13                 9    30.77%         200               145    27.50%           0                 0         -
mediad/src/exposure.rs                    412               161    60.92%          28                 8    71.43%         279               110    60.57%           0                 0         -
mediad/src/main.rs                        376               376     0.00%          10                10     0.00%         254               254     0.00%           0                 0         -
mediad/src/pipeline.rs                   1622              1410    13.07%         108                86    20.37%        1051               905    13.89%           0                 0         -
mediad/src/producer.rs                    161                33    79.50%          20                 5    75.00%         109                20    81.65%           0                 0         -
mediad/src/relay.rs                      2217               234    89.45%         179                28    84.36%        1448               171    88.19%           0                 0         -
mediad/src/route.rs                       176                19    89.20%          12                 0   100.00%         151                 8    94.70%           0                 0         -
mediad/src/session.rs                     671                21    96.87%          37                 0   100.00%         427                18    95.78%           0                 0         -
mediad/src/stream.rs                      826               129    84.38%          55                 9    83.64%         521                87    83.30%           0                 0         -
mediad/src/turn.rs                        420                31    92.62%          39                 3    92.31%         254                16    93.70%           0                 0         -
mediad/src/upstream.rs                    263                16    93.92%          17                 1    94.12%         160                12    92.50%           0                 0         -
mediad/src/web.rs                         119                26    78.15%          15                 4    73.33%          82                17    79.27%           0                 0         -
odometry/src/lib.rs                       321                 5    98.44%          22                 1    95.45%         192                 7    96.35%           0                 0         -
pad-imu/src/lib.rs                        608                 7    98.85%          39                 0   100.00%         372                 5    98.66%           0                 0         -
padd/src/main.rs                         1088               606    44.30%          36                13    63.89%         664               417    37.20%           0                 0         -
padd/src/tap.rs                          1253               682    45.57%          73                36    50.68%         767               407    46.94%           0                 0         -
pet-detect/src/bin/detect.rs               61                61     0.00%           1                 1     0.00%          38                38     0.00%           0                 0         -
pet-detect/src/bin/features.rs             46                46     0.00%           2                 2     0.00%          20                20     0.00%           0                 0         -
pet-detect/src/lib.rs                     362               184    49.17%          27                13    51.85%         224               122    45.54%           0                 0         -
pet-detect/src/worker.rs                  381               230    39.63%          24                14    41.67%         276               160    42.03%           0                 0         -
robotctl/src/configure.rs                1596               477    70.11%          61                12    80.33%         819               322    60.68%           0                 0         -
robotctl/src/duck.rs                     1211               144    88.11%          63                 1    98.41%         604                47    92.22%           0                 0         -
robotctl/src/imu_view.rs                  266                25    90.60%          19                 2    89.47%         173                19    89.02%           0                 0         -
robotctl/src/main.rs                     5088              2726    46.42%         277               126    54.51%        3467              1855    46.50%           0                 0         -
robotctl/src/monitor.rs                  4308               748    82.64%         222                36    83.78%        2721               477    82.47%           0                 0         -
robotctl/src/path_map.rs                  425                59    86.12%          24                 2    91.67%         223                36    83.86%           0                 0         -
robotctl/src/show.rs                      714                46    93.56%          31                 1    96.77%         509                25    95.09%           0                 0         -
robotd-params/src/edit.rs                1904               121    93.64%         114                14    87.72%         929                53    94.29%           0                 0         -
robotd-params/src/lib.rs                 1976                95    95.19%         182                 9    95.05%        1420                61    95.70%           0                 0         -
robotd-params/src/registry.rs             193                10    94.82%          15                 2    86.67%         144                17    88.19%           0                 0         -
robotd/src/chorale.rs                    1366                30    97.80%          71                 4    94.37%         826                20    97.58%           0                 0         -
robotd/src/control.rs                     450               411     8.67%          29                24    17.24%         355               307    13.52%           0                 0         -
robotd/src/intents.rs                     419                74    82.34%          48                 9    81.25%         308                49    84.09%           0                 0         -
robotd/src/main.rs                       7308              1611    77.96%         356                54    84.83%        4724              1028    78.24%           0                 0         -
robotd/src/soc.rs                          47                25    46.81%           4                 1    75.00%          30                15    50.00%           0                 0         -
robotd/src/sound.rs                       968               692    28.51%          50                34    32.00%         565               420    25.66%           0                 0         -
robotd/src/theremin.rs                    472               112    76.27%          32                12    62.50%         271                64    76.38%           0                 0         -
sounds/src/chorale/beat.rs                403                16    96.03%          26                 1    96.15%         246                12    95.12%           0                 0         -
sounds/src/chorale/midi.rs               1047                80    92.36%          44                 2    95.45%         581                43    92.60%           0                 0         -
sounds/src/chorale/mod.rs                1423                57    95.99%          83                 3    96.39%         812                41    94.95%           0                 0         -
sounds/src/chorale/text.rs                700                60    91.43%          41                12    70.73%         376                17    95.48%           0                 0         -
sounds/src/lib.rs                         167                35    79.04%          14                 6    57.14%          87                21    75.86%           0                 0         -
sounds/src/main.rs                        463               463     0.00%          20                20     0.00%         240               240     0.00%           0                 0         -
sounds/src/personality.rs                 174                 0   100.00%           6                 0   100.00%          84                 0   100.00%           0                 0         -
sounds/src/rng.rs                         173                 0   100.00%          18                 0   100.00%          96                 0   100.00%           0                 0         -
sounds/src/stream.rs                      719                 7    99.03%          46                 1    97.83%         420                 6    98.57%           0                 0         -
sounds/src/synth.rs                       353                 7    98.02%          29                 0   100.00%         197                 3    98.48%           0                 0         -
sounds/src/voices.rs                      679                 2    99.71%          26                 0   100.00%         353                 2    99.43%           0                 0         -
test-support/src/lib.rs                   272                 2    99.26%          22                 0   100.00%         167                 0   100.00%           0                 0         -
tof/src/config.rs                          42                 8    80.95%           4                 1    75.00%          23                 7    69.57%           0                 0         -
tof/src/imu.rs                            223               153    31.39%          10                 5    50.00%         137                93    32.12%           0                 0         -
tof/src/lib.rs                             88                 0   100.00%           9                 0   100.00%          54                 0   100.00%           0                 0         -
tof/src/main.rs                           770               687    10.78%          37                26    29.73%         481               437     9.15%           0                 0         -
tof/src/sensor.rs                         230               148    35.65%          22                15    31.82%         163               114    30.06%           0                 0         -
tof/src/status.rs                          76                 2    97.37%           7                 1    85.71%          58                 1    98.28%           0                 0         -
updater/src/account.rs                     87                 0   100.00%           9                 0   100.00%          76                 0   100.00%           0                 0         -
updater/src/config.rs                     436                24    94.50%          38                 4    89.47%         354                17    95.20%           0                 0         -
updater/src/engine.rs                    3239               473    85.40%         215                27    87.44%        2092               288    86.23%           0                 0         -
updater/src/faults.rs                      69                 7    89.86%           7                 0   100.00%          51                 0   100.00%           0                 0         -
updater/src/fsutil.rs                     104                30    71.15%          10                 6    40.00%          51                24    52.94%           0                 0         -
updater/src/hooks.rs                      454                15    96.70%          37                 2    94.59%         382                12    96.86%           0                 0         -
updater/src/ipc.rs                        900               308    65.78%          67                20    70.15%         557               167    70.02%           0                 0         -
updater/src/journal.rs                    832                78    90.62%          57                10    82.46%         483                53    89.03%           0                 0         -
updater/src/lib.rs                         55                16    70.91%           5                 0   100.00%          41                14    65.85%           0                 0         -
updater/src/main.rs                       550               222    59.64%          38                14    63.16%         420               151    64.05%           0                 0         -
updater/src/manifest.rs                   159                 6    96.23%          16                 0   100.00%         110                 1    99.09%           0                 0         -
updater/src/orphan.rs                     288                 6    97.92%          25                 0   100.00%         181                 3    98.34%           0                 0         -
updater/src/policy.rs                    1532               582    62.01%         121                49    59.50%         819               333    59.34%           0                 0         -
updater/src/preflight.rs                  382                16    95.81%          47                 4    91.49%         284                11    96.13%           0                 0         -
updater/src/reconcile.rs                  292                15    94.86%          25                 3    88.00%         172                 5    97.09%           0                 0         -
updater/src/robot.rs                      225                33    85.33%          37                10    72.97%         139                21    84.89%           0                 0         -
updater/src/source/github.rs              474               169    64.35%          51                26    49.02%         304                99    67.43%           0                 0         -
updater/src/source/hf_hub.rs              136                72    47.06%          20                12    40.00%          81                41    49.38%           0                 0         -
updater/src/source/http.rs                319                82    74.29%          29                11    62.07%         260                88    66.15%           0                 0         -
updater/src/source/local.rs               346                37    89.31%          36                 9    75.00%         192                26    86.46%           0                 0         -
updater/src/source/mod.rs                  39                30    23.08%           4                 2    50.00%          30                24    20.00%           0                 0         -
updater/src/spawn.rs                       94                13    86.17%           5                 0   100.00%          49                 4    91.84%           0                 0         -
updater/src/store.rs                      593                41    93.09%          43                 7    83.72%         271                42    84.50%           0                 0         -
updater/src/transcript.rs                 518                42    91.89%          31                 3    90.32%         290                22    92.41%           0                 0         -
updater/src/unix.rs                        44                 3    93.18%           3                 0   100.00%          22                 2    90.91%           0                 0         -
updater/src/verify.rs                     786                95    87.91%          55                18    67.27%         424                75    82.31%           0                 0         -
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TOTAL                                   79686             21909    72.51%        5238              1393    73.41%       49784             14072    71.73%           0                 0         -

`duckctl ssh` already resolves the robot's address and hands the terminal
over; copying a file off a robot still meant `scp radxa@$(duckctl ip):…`,
which is the substitution `ssh` exists to remove.

The robot side is `scp`'s own `host:path` with the host left out, so
`duckctl scp report.md :/tmp/` sends one up and
`duckctl scp :/var/log/robotd.log .` brings one down. The rewrite is
textual and only ever looks at the first character, which is what keeps
`-r`, `-P 2222`, several sources and a path with a colon in the middle of
it working without this tool having to understand any of them.

Both directions, then, out of one command that answers to the name people
reach for — rather than a `push` and a `pull` that nobody typing `scp`
would find.

A copy with no `:` anywhere in it is refused before the radio is turned
on. `scp` performs that copy happily, locally, and nothing in its output
says the robot was never involved; refusing it after the scan would also
charge eight seconds for the diagnosis.

The `exec` that makes `ssh` become `ssh` is now `become_program`, shared
by both, so the progress meter, the key prompts and the exit status are
`scp`'s own for the same reason they were ssh's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@pierre-rouanet
pierre-rouanet merged commit 6507d2e into main Sep 10, 2026
4 checks passed
@pierre-rouanet
pierre-rouanet deleted the duckctl-scp branch September 10, 2026 15:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant