CLI Reference

Every psf-guard command: serve the UI, import image folders, screen frames, export, remove rejects, sync, back up, repair calibration duplicates, measure flat groups, manage users, and analyze single files. The desktop app needs none of them.

CLI / NAS
Run PSF Guard on a server or from scripts

psf-guard --help lists the commands; psf-guard <command> --help lists a command's options.

Download the CLI →

Commands

CommandDoes
serverServe the grader UI, the REST API and the MCP endpoint
create-dbCreate a Target Scheduler database and import image folders into it
importImport image folders into an existing database
remove-importedRemove the projects an import created
screen-fitsScreen frames for occlusion, clouds, pointing and satellite risk
exportCopy graded lights and matched calibration frames into a stacking folder
astrobin-csvWrite AstroBin's acquisition CSV for a project or target
stack-snrMeasure how a stack's SNR grows with depth, from a folder
move-rejectsMove rejected frames out of the stacking tree
restore-rejectsBring archived rejects back
remove-rejectsTake old rejects out of the catalog, their files to a trash folder
list-removed, restore-removedList removal batches; bring one back
empty-reject-trash, purge-removedDelete trashed files past their time; then forget their saved rows
filter-rejectedLegacy reject mover with statistical flags
sync pull, planning, grades, remoteCopy captures, plans or grades between two databases or two servers
backupBack up the settings and every catalog into one zip archive
calibration repair-duplicatesMerge calibration rows that record one exposure more than once
flat-groupsMeasure a catalog's flat groups on a copy
usersManage browser users and API tokens
sky-maps install, listInstall N.I.N.A.'s offline sky maps for framing
fill-guidsGive Target Scheduler rows without a GUID a new one
list-projects, list-targetsList a database's projects, or a project's targets
dump-gradingPrint grading results
show-imagesShow images by ID
update-gradeSet one image's grade
regradeRegrade by statistical analysis
analyze-fitsDetect stars and compare with the database's values
annotate-starsWrite a PNG with detected stars marked
visualize-psf, visualize-psf-multiShow PSF fit residuals for one star or a grid
benchmark-psfTime PSF fitting
benchmark-detectionBenchmark HocusFocus star detection over a frame manifest
stretch-to-pngConvert a FITS file to a stretched PNG
read-fitsPrint FITS metadata

Commands that open a catalog take a registry slug or a .sqlite path, and --registry picks a registry other than the default. The older database commands instead read the global -d option, which goes before the command name and defaults to schedulerdb.sqlite: psf-guard -d my.sqlite list-projects.

Serve the grader UI

# Serve; the database is registered on first run and reused afterwards
psf-guard server <database> <image-dirs...> [--port 3000]

# Serve every database in the registry
psf-guard server

# Server settings from a TOML file
psf-guard server --config psf-guard.toml

# A throwaway session that leaves your real registry alone
psf-guard server --registry /tmp/scratch.json <db> <dirs...>

# Listen on localhost only (the default is 0.0.0.0)
psf-guard server --host 127.0.0.1 <db> <dirs...>
OptionDoes
--config, -cRead the server's TOML file
--registryUse another database registry
--port, -p; --hostWhere to listen
--cache-dirCache folder
--stack-dirFolder for stacks; default Settings, else the cache
--calibration-dirFolder for calibration masters; default Settings, else the cache
--pregenerate-screen, --pregenerate-large, --pregenerate-original, --pregenerate-annotated, --pregenerate-allMake those previews in the background
--cache-expiryHow long pre-generated previews last (default 1y)
--allow-database-managementLet editors name server paths from the browser: databases and their folders, imports from other folders, local exports, storage, PixInsight. Flat-group choices, duplicate repair, backups and Planning's activation need it too. On a network address it also needs an account. See Server Accounts.
--director-metaKeep the planning store at another path; needs --allow-database-management
--allow-anonymous-accessServe a network address with no accounts, open to everyone
--static-dirServe the frontend from a folder instead of the built-in copy

The server looks for each frame in its image folders in order and uses the first match. It serves every database in the registry, not only the one on the command line. See Configuration & API, Server Accounts and Storage & Performance.

Import image folders

# Create and register a catalog from image folders
psf-guard create-db archive.sqlite ./lights ./more-lights --name "Archive"

# Preview, then import lights and calibration frames
psf-guard import archive ./new-frames --dry-run
psf-guard import archive ./new-frames

# One frame kind only
psf-guard import archive ./new-lights --lights-only
psf-guard import archive ./new-flats --calibration-only

# Leave out integration masters and other processing output
psf-guard import archive ./processing-tree --skip-processed

# Take frames from a rig this catalog has not recorded
psf-guard import archive ./new-camera --accept-other-rigs

# Leave out folders that match a pattern; repeat for more
psf-guard import archive ./new-frames --exclude '**/Hocus Focus/**'

# Preview removing the projects an import made
psf-guard remove-imported archive --dry-run

The first pass reads headers only. Lights arrive as Pending frames and build projects, targets, shared exposure templates and plans. Bias, dark, dark-flat and flat frames go to the calibration library, not Target Scheduler's image table. New frames attach to an existing target by name or coordinates (--match-radius-deg) unless you pass --no-attach. Calibrated and registered copies from WBPP, Siril, ASTAP or DeepSkyStacker pair with the light they came from; --no-pairing imports them as lights instead.

See Import Images.

Screen frames

psf-guard screen-fits ./lights                        # a verdict per frame
psf-guard screen-fits ./lights --annotate ./diag      # diagnostic PNGs
psf-guard screen-fits ./lights --regrade-db my-db --dry-run
psf-guard screen-fits ./lights --format json          # or table, csv

screen-fits needs no database for spatial and photometric screening. With --regrade-db it reads target coordinates from the catalog, plate-solves each frame with Seiza, adds cached satellite risk, and writes supported rejections. Satellite predictions only warn; a rejection needs a matching bright trail in the pixels.

OptionDoes
--min-scoreReject below this quality score (default 0.35)
--dead-cell-riseRise in dead cells over the baseline that flags occlusion (default 0.08)
--max-hfr, --min-starsHard limits, as in N.I.N.A.'s subframe selection; off by default
--ignore-satellitesKeep satellite trails as warnings only
--session-gapMinutes that split a night into sequences (default 60)
--threadsWorker threads (default: all cores, within memory)
--cache-dirThe server's cache, for orbital elements and predictions

See Quality Screening, Astrometry Quality and Satellite Tracks.

Export for stacking

# Preview, then copy Accepted lights into <target>/LIGHT/<filter>/
psf-guard export my-db --dest ./stacking --dry-run
psf-guard export my-db --dest ./stacking

# One target and filter, hardlinked where possible
psf-guard export my-db --dest ./stacking \
  --target "California Nebula" --filter HA --link

# A WBPP tree that points to the frames where they are
psf-guard export my-db --dest ./wbpp --layout wbpp --placement reference \
  --wbpp-drizzle 2x

Rejects are never exported; --include-pending adds ungraded frames. Re-runs skip files already there.

OptionValues
--layoutstandard (by target) or wbpp (one root per frame type)
--placementcopy, hardlink, reflink, symlink, or reference (place nothing; the WBPP runner points to the originals). --link is hardlink.
--local-root, --remote-rootWith reference: the frames' folder as this machine and as the PixInsight machine see it
--wbpp-qualitymaximum, good, fast
--wbpp-fast-integrationoff (default), auto, on
--wbpp-drizzleoff, 2x, 3x
--wbpp-autocroptrue or false
--wbpp-rejectionpercentile-clip, winsorized-sigma, linear-fit, esd, rcr, auto

See Export & AstroBin and WBPP Runs.

AstroBin acquisition CSV

psf-guard astrobin-csv my-db --project-id 2 -o acquisitions.csv
psf-guard astrobin-csv my-db --target-id 42 --detail full --filter-id Ha=1234

One row per night, filter and exposure length. essentials (the default) writes date, filter, count and duration; full adds binning, gain, sensor and ambient temperature, the f-number, and the calibration frames the library matches. --filter-id NAME=ID gives a filter its AstroBin ID, over the registry's settings.

Measure stack depth from a folder

# A folder in capture order
psf-guard stack-snr ./lights

# Strongest frames first, and save the curve
psf-guard stack-snr ./lights --order quality --csv curve.csv --json curve.json

# Weight each frame by its noise
psf-guard stack-snr ./lights --weight-by-noise

stack-snr searches folders for FITS and XISF files, stacks them raw, and measures SNR at each depth. Capture order asks whether another night would help; quality order ranks frames by stars and sharpness and asks which are worth keeping. --weight-by-noise weighs each frame by its inverse noise variance, relative to the first frame, as the stacking method's By noise does, and prints each weight. --threads caps star detection threads. A curve needs three frames or more. See Stacks.

Archive or remove rejects

psf-guard move-rejects --db <slug> [--dry-run] [--project NAME] [--target NAME]
psf-guard restore-rejects --db <slug> [--all] [--image-id N] [--guid GUID] [--dry-run]

move-rejects also takes --reject-segment, --reject-depth and --sidecar-exts. See Rejects & Sync.

# Preview, then remove frames rejected 7 days ago or more
psf-guard remove-rejects --db <slug> [--days 7] [--project-id N] [--target-id N]
psf-guard remove-rejects --db <slug> --apply [--retention-days 14] --cache-dir /path/to/cache

psf-guard list-removed --db <slug>
psf-guard restore-removed --db <slug> (--batch ID | --guid GUID...)
psf-guard empty-reject-trash --db <slug>
psf-guard purge-removed --db <slug> [--batch ID]

remove-rejects only previews until you pass --apply. Files go to a .psf-guard-trash folder in the image directory and stay --retention-days (14 by default) before empty-reject-trash may delete them; until then restore-removed brings them back. --cache-dir points to the server's cache (default ./cache), so the frames' cached files go too. purge-removed forgets the saved rows of emptied batches. Settings does the same under Purge Rejects; see Rejects & Sync.

Sync two catalogs

psf-guard sync pull --from telescope.sqlite --to my-db
psf-guard sync planning --from my-db --to telescope.sqlite
psf-guard sync grades --from my-db --to telescope.sqlite

# With another PSF Guard over HTTP
psf-guard sync remote --direction pull --local my-db \
  --peer https://telescope.example:3000 --token-file key.txt

See Rejects & Sync.

Back up settings and catalogs

psf-guard backup /mnt/backups/psf-guard                  # settings and catalogs
psf-guard backup /mnt/backups/psf-guard --scope meta     # settings only
psf-guard backup /mnt/backups/psf-guard --dry-run
psf-guard backup /mnt/backups/psf-guard --registry /var/lib/psf-guard/registry.json \
  --config /etc/psf-guard/psf-guard.toml

Catalogs are copied with SQLite's online backup, so it can run while the server and N.I.N.A. write. It exits non-zero when it writes no archive; a catalog it skipped is named in the manifest. The schedule never prunes archives this command makes. See Backups for every option, a completeness check and scheduled runs from cron, systemd, Docker or Task Scheduler.

Work on the calibration library

New in 0.12. calibration repair-duplicates and flat-groups work on a catalog's calibration library from the command line.

Repair duplicates

# Preview, then merge rows that record one exposure more than once
psf-guard calibration repair-duplicates /data/catalog.sqlite --dry-run
psf-guard calibration repair-duplicates /data/catalog.sqlite

# A dry run on a catalog copied from another machine
psf-guard calibration repair-duplicates copy.sqlite --dry-run \
  --map-prefix /mnt/share=/Volumes/share

repair-duplicates takes the catalog's .sqlite path. It keeps the oldest row of each exposure, moves that row to a copy still on disk when its own file is gone, and removes the masters built from merged rows so stacks build them again. No file is moved or deleted. --dry-run prints the plan and writes nothing; --map-prefix FROM=TO, with --dry-run only, reads the recorded paths through this machine's mount. Progress and timings go to standard error.

A repair holds a lock file beside the catalog (<catalog>.psf-guard-repair.lock), so the command line and a server never repair one catalog at once. A server open on the catalog sees the change at its next scan, regrade or restart. The Calibration page's Repair duplicates… does the same; see Calibration Libraries.

Measure flat groups on a copy

psf-guard flat-groups /data/catalog.sqlite --work /tmp/flat-work
psf-guard flat-groups /data/catalog.sqlite --work /tmp/flat-work \
  --images /data/images --lights --report flat-groups.json

flat-groups opens the catalog read-only, copies it into the --work folder, and measures there. It prints each family's groups, the splits between them and why, the rig's changes, and the angle difference. It never writes the catalog or a frame.

OptionDoes
--workFolder for the copy, the measurements and the report. A later run reuses the copy there.
--imagesImage folders that hold the lights, for their pixel evidence and --lights; repeat for more
--lightsAlso say how each light's flats are chosen; reads each light's header
--map-prefix FROM=TORewrite calibration paths in the copy when the catalog was made on another machine; repeat for more
--reportWrite the report as JSON
--freshCopy the catalog again and measure everything again

See Calibration Libraries.

Manage users and API tokens

psf-guard users add editor --role read-write [--email ...] [--password-file PATH] [--replace]
psf-guard users add viewer --role read-only
psf-guard users list
psf-guard users remove viewer [--allow-empty]

psf-guard users token create editor --label "claude on laptop" [--read-only] [--expires-days 90]
psf-guard users token list [--user editor]
psf-guard users token revoke <id>

Without --password-file, users add asks for the password twice. token create prints the token once. Each command takes --registry; restart the server after a change. See Server Accounts and MCP for Agents.

Install offline sky maps

psf-guard sky-maps install full --cache-dir /path/to/cache
psf-guard sky-maps install nsns-ohs --cache-dir /path/to/cache
psf-guard sky-maps install nsns-ohs-starless --cache-dir /path/to/cache
psf-guard sky-maps list --cache-dir /path/to/cache

full is N.I.N.A.'s whole-sky DSS set (3.3 GB), nsns-ohs the Northern Sky Narrowband Survey in SHO colour (1.4 GB), and nsns-ohs-starless its starless version (0.5 GB). Any other value is taken as a zip URL. Pass the cache the server uses; the default is ./cache. See Configuration & API.

Detect stars and analyze PSFs

# Detect stars and compare with the database's values
psf-guard analyze-fits image.fits [--detector nina|hocusfocus] [--compare-all]

# Annotated star map
psf-guard annotate-stars image.fits [--max-stars 50]

# PSF fit residuals: one star, or a grid
psf-guard visualize-psf image.fits [--star-index N]
psf-guard visualize-psf-multi image.fits [--num-stars 25]

# Timing
psf-guard benchmark-psf image.fits
psf-guard benchmark-detection manifest.json --output benchmark-detection.csv
PSF visualization grid: observed, fitted, residual
visualize-psf-multi draws observed, fitted and residual grids with Moffat and Gaussian models.

These commands read XISF files too. annotate-stars labels each star with its HFR; --max-stars sets how many. benchmark-detection records star count, HFR and time per frame from a JSON manifest, and agreement with N.I.N.A.'s values where the manifest has them.

FITS utilities

psf-guard stretch-to-png image.fits -o output.png    # MTF auto-stretch
psf-guard read-fits image.fits [-v]                  # header and metadata

Query databases and grade by hand

psf-guard -d database.sqlite list-projects
psf-guard -d database.sqlite list-targets "Project Name"
psf-guard -d database.sqlite dump-grading [--status rejected] [--project NAME] [--format csv]
psf-guard -d database.sqlite show-images 12,13,14
psf-guard -d database.sqlite update-grade 123 rejected --reason "Clouds"
psf-guard -d database.sqlite fill-guids [--dry-run]
psf-guard regrade database.sqlite [--dry-run] [--days 90]     # statistical regrade

fill-guids gives Target Scheduler rows that lack a GUID a new one, after copying the database beside itself. Close N.I.N.A. on that rig first. See Configuration.

regrade and filter-rejected take statistical outlier flags: --enable-statistical with --stat-hfr, --stat-stars, --stat-distribution and --stat-clouds, for HFR and star-count outliers per target and filter and for cloud onsets in a sequence. See docs/STATISTICAL_GRADING.md.

More projects from theatr.us