File operations
Every lockbox container has 15 built-in commands for moving files in and out of /work and managing them there. They are part of lockbox-wrapper and work in every downstream image, whatever its allowlist holds. A built-in always wins over an allowed.json entry with the same name.
Paths
Every path is relative to /work:
notes.txtand/notes.txtboth mean/work/notes.txt. A leading/is stripped and the rest goes under/work, soget /etc/passwdlooks for/work/etc/passwd.- The wrapper resolves
..and symlinks withrealpathbefore it checks the result. Anything that lands outside/workfails withError: path outside /work(orsource path outside /work/destination path outside /workfor two-path commands). - An empty path means
/workitself.
search-files is the exception: its pattern is not checked this way. See search-files.
Files and directories the wrapper creates get mode 664 and 775, because it sets umask 002.
Errors and exit codes
A failed command prints Error: <message> on stderr and exits 1. Success exits 0. The messages are listed with each command below and in Troubleshooting.
Command list
| Command | Arguments | What it does |
|---|---|---|
put | <path> | Write stdin to a file, replacing it if it exists |
get | <path> | Write a file to stdout |
append-file | <path> | Append stdin to an existing file |
list-files | [--json] [path] | List /work or a subdirectory |
file-info | <path> | JSON metadata for one file or directory |
file-exists | <path> | Print true or false |
file-hash | <path> | SHA-256 hex digest of a file |
disk-usage | [path] | Total bytes of the files under a path |
search-files | <pattern> | Recursive glob search |
create-dir | <path> | Create a directory and any missing parents |
remove-file | <path> | Delete a file |
remove-dir | <path> | Delete an empty directory |
remove-dir-recursive | <path> | Delete a directory and everything in it |
move-file | <src> <dst> | Move or rename a file or directory |
copy-file | <src> <dst> | Copy a file |
The examples use lockbox@host. Use your image’s username and port.
put
ssh lockbox@host "put input.txt" < input.txt
Reads stdin in 64 KiB chunks and writes it to the file. An existing file is replaced. The parent directory must exist: put a/b/c.txt with no a/b fails with parent directory does not exist. Run create-dir first.
get
ssh lockbox@host "get output.txt" > output.txt
Streams the file to stdout unchanged, so binary files are safe. Fails with no such file if the path is missing or is a directory.
append-file
echo "more data" | ssh lockbox@host "append-file output.txt"
Appends stdin to the end of the file. The file must already exist (no such file otherwise); use put to create it.
list-files
ssh lockbox@host "list-files"
ssh lockbox@host "list-files subdir"
ssh lockbox@host "list-files --json"
Prints one entry per line, sorted by name, with a trailing / on directories. Hidden files are included. Pointing it at a file prints just that file’s name. Fails with no such file or directory for a missing path.
--json can go before or after the path. It prints a JSON array with one object per entry, in the same format as file-info:
[
{
"name": "a.txt",
"size": 3,
"modified": 1794295298,
"isDir": false,
"mode": "rw-rw-r--",
"owner": "lockbox",
"group": "lockbox",
"links": 1
}
]
file-info
ssh lockbox@host "file-info output.txt"
Prints one JSON object on one line:
| Field | Meaning |
|---|---|
name | Base name |
size | Size in bytes |
modified | Modification time, Unix seconds |
isDir | true for a directory |
mode | Permission bits as rwxr-xr-x, without the file type character |
owner, group | Names, or the numeric ID when the container has no such user or group |
links | Hard link count |
Symlinks are followed, so the fields describe the target. Fails with no such file or directory.
file-exists
ssh lockbox@host "file-exists output.txt"
Prints true or false and exits 0 either way. A path outside /work prints false instead of an error.
file-hash
ssh lockbox@host "file-hash output.txt"
Prints the SHA-256 digest as 64 lowercase hex characters. Fails with no such file for a missing path or a directory.
disk-usage
ssh lockbox@host "disk-usage"
ssh lockbox@host "disk-usage subdir"
Prints a byte count. For a file, that is its size. For a directory, it is the sum of the sizes of every file under it, so it counts content, not disk blocks the way du does. Symlinked directories are not followed.
search-files
ssh lockbox@host "search-files **/*.txt"
Runs a Python glob with ** matching any number of directories, starting from /work. Prints matches relative to /work, sorted, with a trailing / on directories. Names starting with . are not matched. No match prints nothing and exits 0.
The pattern does not go through the /work check the other commands use. search-files ../etc/* and search-files /etc/* print file names outside /work. They cannot read the files, but they do reveal what exists in the container. See Security.
create-dir
ssh lockbox@host "create-dir project1/raw"
Creates the directory and any missing parents, like mkdir -p. An existing directory is not an error.
remove-file
ssh lockbox@host "remove-file input.txt"
Deletes a file. Fails with is a directory, not removing on a directory and no such file or directory on a missing path. Because the path is resolved first, pointing it at a symlink deletes the file the symlink points to, as long as that file is inside /work.
remove-dir
ssh lockbox@host "remove-dir project1"
Deletes an empty directory. Fails with directory not empty, not a directory, or cannot remove /work.
remove-dir-recursive
ssh lockbox@host "remove-dir-recursive project1"
Deletes the directory and everything in it, with no confirmation. Refuses /work itself with cannot remove /work, and fails with not a directory on a file.
move-file
ssh lockbox@host "move-file old.txt new.txt"
Renames a file or a directory. The source must exist and the destination’s parent directory must exist (destination parent directory does not exist). An existing destination file is replaced.
copy-file
ssh lockbox@host "copy-file original.txt backup.txt"
Copies a file, keeping its modification time and mode. Directories are not supported: the source must be a file (no such file otherwise). If the destination is an existing directory, the copy goes inside it under the same name.