Skip to content

Command-line interface and executable walkthrough

Install protein-fasta[cli]. The wheel declares one console-script entry point:

protein-fasta = protein_fasta.cli:main

Every operation is a Cyclopts subcommand of protein-fasta, not a separate installed script. Parameters are generated by Cyclopts, and progress/reporting goes through Loguru. Run protein-fasta --help or protein-fasta <command> --help for the generated option reference.

Command map

Area Command syntax Result
FASTA table basic FASTA-PATH TABLE-PATH Stable id, description, and optional sequence table
FASTA table table [OPTIONS] FASTA-PATH TABLE-PATH Row-wise built-in enrichment, or whole-file enrichment with --strict
FASTA table configured --rules PATH... --classifiers PATH [OPTIONS] FASTA-PATH TABLE-PATH Enrichment using explicit parser and classifier documents
FASTA table formats FASTA-PATH TABLE-PATH One recognition-diagnostic row per built-in format
FASTA table database [--formats NAME...] PARQUET-PATH FASTA-PATH... Parsed ProteinDatabase frame that parse and its consumers read in place of the FASTA files
FASTA evidence diagnostics FASTA-PATH Record, residue, length, namespace, normalization, and classification summary
FASTA evidence checksum FASTA-PATH Exact-file MD5 and normalized ID/sequence-content fingerprint
Peptide table digest [OPTIONS] FASTA-PATH TABLE-PATH Theoretical peptides and missed-cleavage counts
UniProt uniprot-catalog --output DIR or --request PATH Immutable local proteome-catalog snapshot
UniProt uniprot-proteomes [OPTIONS] CATALOG-PATH TABLE-PATH Local catalog filter; no provider request
UniProt uniprot-download PROTEOME MODE or --request PATH One acquired source FASTA plus reproducible request/evidence; no build or decoys
Protein input prepare FASTA OUTPUT --id ID or --request PATH Canonical protein-input Parquet from ordered FASTA sources
Protein input derive-input INVENTORY OUTPUT --id ID or --request PATH Clean protein-input Parquet derived from existing inventories
Database build PROTEIN-INPUT --output DIR ... or --request PATH Decoy-free biological FASTA and protein inventory
Database decoy BIOLOGICAL-INVENTORY --output FASTA --method METHOD or --request PATH Search FASTA and search inventory for one required strategy
Peptides peptides INVENTORY --output DIR or --request PATH Peptide inventory, mapping, and unique-peptide FASTA
Peptides pepcompare PEPTIDES-A PEPTIDES-B --output PATH or --request PATH Exact comparison of two canonical peptide inventories
Decoy analysis decoy-report BIOLOGICAL-INVENTORY --output PATH --method METHOD or --request PATH Peptide-level comparison of requested decoy methods
Registry index [OPTIONS] FASTA-DIR REGISTRY-PATH Build or update a SQLite or DuckDB registry from FASTA
Registry index-inventory [OPTIONS] INVENTORY-PATH REGISTRY-PATH Index one canonical biological or search inventory
Registry candidate INVENTORY REGISTRY --output PATH or --request PATH Compare a candidate inventory without registering it
Registry registry REGISTRY-PATH TABLE-PATH Export database summaries and fingerprints
Registry compare [OPTIONS] REGISTRY-PATH DATABASE-ID TABLE-PATH Compare one indexed database with every detailed database
Registry pairs [OPTIONS] REGISTRY-PATH OUTPUT Export long pair metrics and optional Jaccard matrices
Registry cluster [OPTIONS] REGISTRY-PATH TABLE-PATH Export deterministic average-linkage merge rows

Command and artifact rules

  • CSV, TSV, XLSX, and Parquet table formats are selected by output suffix.
  • --no-sequence applies to protein-row exports. It is not a generic output switch.
  • Request-document paths are resolved relative to the document containing them.
  • A direct workflow command writes a validated authored *.request.json before computation. --request PATH replays it; --output may redirect replayed products without changing policy.
  • Authored requests are never overwritten. Use --save PATH to choose their first-run location.
  • prepare writes the canonical protein-input Parquet. build consumes it and produces only a decoy-free biological FASTA and protein inventory.
  • decoy is a subsequent operation over the biological inventory. Its request must name exactly one strategy; callers that need only the biological database do not invoke it.
  • candidate is read-only. index-inventory is the preferred post-approval registry handoff.
  • SQLite and DuckDB are selected by registry suffix and, when creating a registry, matching registry configuration.

UniProt catalog and acquisition

Catalog synchronization and FASTA download contact UniProt and therefore are not run during a documentation build. Filtering an existing catalog is local, but it requires the snapshot created by uniprot-catalog.

Start a reference-proteome catalog directly:

protein-fasta uniprot-catalog --output catalog

Before contacting UniProt, this writes catalog/uniprot-catalog.request.json:

{
  "output_dir": ".",
  "schema_version": "0.1",
  "selection": {
    "type": "reference"
  },
  "timeout_seconds": 120.0
}
  • schema_version selects the validated catalog-request contract.
  • output_dir is relative to the request file, so . means its catalog/ directory.
  • selection.type requests the UniProt reference-proteome catalogue.
  • timeout_seconds records the provider timeout used by the first run.

Replay the saved selection, optionally redirecting the snapshot:

protein-fasta uniprot-catalog \
  --request catalog/uniprot-catalog.request.json \
  --output replayed-catalog

The direct download form takes a proteome identifier and one acquisition mode:

  • reviewed — reviewed Swiss-Prot entries;
  • canonical — the complete proteome's reviewed and unreviewed canonical entries; or
  • opg — one canonical protein sequence per gene from UniProt GeneCentric.

For example:

protein-fasta uniprot-download UP000000589 opg --output mouse-opg.fasta
head -n 3 mouse-opg.fasta

Before acquisition, this writes mouse-opg.fasta.request.json:

{
  "acquisition": {
    "type": "one_seq_per_gene"
  },
  "output_fasta": "mouse-opg.fasta",
  "schema_version": "0.1",
  "selection": {
    "proteome_id": "UP000000589",
    "type": "proteome_id"
  },
  "timeout_seconds": 120.0
}
  • selection.type says the source is an explicit proteome rather than a taxonomy search.
  • selection.proteome_id records the chosen proteome.
  • acquisition.type records the canonical-per-gene behavior selected by opg.
  • output_fasta is the FASTA path relative to the request.
  • timeout_seconds and schema_version have the same meanings as in the catalog request.

Replay it explicitly:

protein-fasta uniprot-download \
  --request mouse-opg.fasta.request.json \
  --output replayed-mouse-opg.fasta

After catalog synchronization, local filtering uses ordinary arguments and no JSON:

protein-fasta uniprot-proteomes uniprot-proteomes.parquet proteomes.csv \
  --query "Homo sapiens"
head -n 3 proteomes.csv

uniprot-download acquires exactly one source FASTA. It does not prepare sources, build a database, add decoys, install, or index. Result evidence records the selected proteome and resolution route, provider query, observed release headers, observed and provider-reported counts, checksum, byte size, and warnings.

Executable offline walkthrough

The remainder of this page executes 21 of the 24 subcommands against two deterministic 50-entry UniProtKB FASTA files and one 50-entry mixed UniProtKB/RefSeq file. The console blocks are not pasted examples: Zensical runs them during make docs, captures their output, and fails the build if any command exits non-zero.

$WORK is a fresh temporary directory used only for this documentation build.

Prepare the isolated example

$ cp -R docs/examples/cli/. "$WORK/"
$ mkdir "$WORK/registry-input"
$ cp "$WORK/alpha.fasta" "$WORK/beta.fasta" "$WORK/registry-input/"

Export the basic table

basic always emits the stable base schema. Here the sequence is deliberately excluded.

$ protein-fasta basic "$WORK/alpha.fasta" "$WORK/basic.csv" --no-sequence
Wrote 50 FASTA records and 2 columns to $WORK/basic.csv
$ head -n 3 "$WORK/basic.csv"
id,description
sp|P00001|PROT00001_HUMAN,Example protein 00001 OS=Homo sapiens OX=9606 GN=GENE00001 PE=1 SV=1
sp|P00002|PROT00002_HUMAN,Example protein 00002 OS=Homo sapiens OX=9606 GN=GENE00002 PE=1 SV=1

Export the automatically enriched table

table recognizes this homogeneous UniProtKB input and adds the configured header fields.

$ protein-fasta table "$WORK/alpha.fasta" "$WORK/proteins.csv" --no-sequence
Wrote 50 FASTA records and 17 columns to $WORK/proteins.csv
$ head -n 3 "$WORK/proteins.csv"
id,description,is_decoy,is_contaminant,is_entrapment,database,review_status,accession,entry_name,entry_mnemonic,organism_mnemonic,protein_name,organism_name,taxonomy_id,gene_name,protein_existence,sequence_version
sp|P00001|PROT00001_HUMAN,Example protein 00001 OS=Homo sapiens OX=9606 GN=GENE00001 PE=1 SV=1,false,false,false,uniprotkb,reviewed,P00001,PROT00001_HUMAN,PROT00001,HUMAN,Example protein 00001,Homo sapiens,9606,GENE00001,1,1
sp|P00002|PROT00002_HUMAN,Example protein 00002 OS=Homo sapiens OX=9606 GN=GENE00002 PE=1 SV=1,false,false,false,uniprotkb,reviewed,P00002,PROT00002_HUMAN,PROT00002,HUMAN,Example protein 00002,Homo sapiens,9606,GENE00002,1,1

Use explicit parser documents

configured applies explicitly selected database-format and entry-classifier JSON documents.

$ protein-fasta configured "$WORK/alpha.fasta" "$WORK/configured.csv" \
  --rules src/protein_fasta/documents/frame_formats/uniprotkb/rules.json \
  --rules src/protein_fasta/documents/frame_formats/refseq/rules.json \
  --classifiers src/protein_fasta/documents/entry_classifiers/rules.json \
  --no-sequence
Wrote 50 FASTA records and 17 columns to $WORK/configured.csv
$ head -n 3 "$WORK/configured.csv"
id,description,is_decoy,is_contaminant,is_entrapment,database,review_status,accession,entry_name,entry_mnemonic,organism_mnemonic,protein_name,organism_name,taxonomy_id,gene_name,protein_existence,sequence_version
sp|P00001|PROT00001_HUMAN,Example protein 00001 OS=Homo sapiens OX=9606 GN=GENE00001 PE=1 SV=1,false,false,false,uniprotkb,reviewed,P00001,PROT00001_HUMAN,PROT00001,HUMAN,Example protein 00001,Homo sapiens,9606,GENE00001,1,1
sp|P00002|PROT00002_HUMAN,Example protein 00002 OS=Homo sapiens OX=9606 GN=GENE00002 PE=1 SV=1,false,false,false,uniprotkb,reviewed,P00002,PROT00002_HUMAN,PROT00002,HUMAN,Example protein 00002,Homo sapiens,9606,GENE00002,1,1

Diagnose a mixed database

The mixed database contains 25 UniProtKB records and 25 RefSeq records. Format detection reports both partial matches.

$ protein-fasta formats "$WORK/mixed.fasta" "$WORK/formats.csv"
Wrote 2 format diagnostics and 4 columns to $WORK/formats.csv
$ head -n 3 "$WORK/formats.csv"
format,matched_rows,total_rows,status
refseq,25,50,partial
uniprotkb,25,50,partial

Automatically parse a mixed database

table applies the sole matching built-in parser to each row. The first preview row is UniProtKB; the second is RefSeq, and both are enriched in one table.

$ protein-fasta table "$WORK/mixed.fasta" "$WORK/mixed-auto.csv"
Wrote 50 FASTA records and 18 columns to $WORK/mixed-auto.csv
$ head -n 3 "$WORK/mixed-auto.csv"
id,description,sequence,is_decoy,is_contaminant,is_entrapment,database,accession,protein_name,organism_name,review_status,entry_name,entry_mnemonic,organism_mnemonic,taxonomy_id,gene_name,protein_existence,sequence_version
sp|P00001|PROT00001_HUMAN,Example protein 00001 OS=Homo sapiens OX=9606 GN=GENE00001 PE=1 SV=1,MPEPTIDEKACDEFGHIKAAR,false,false,false,uniprotkb,P00001,Example protein 00001,Homo sapiens,reviewed,PROT00001_HUMAN,PROT00001,HUMAN,9606,GENE00001,1,1
ref|NP_100001.1|,Example RefSeq protein 00001 [Mus musculus],ACDEFGHIKMPEPTIDERAGK,false,false,false,refseq,NP_100001.1,Example RefSeq protein 00001,Mus musculus,,,,,,,,

Apply only the UniProtKB parser to a mixed database

With only the UniProtKB rule configured, matching rows are enriched. RefSeq rows remain present with id, description, and sequence; the UniProtKB fields are null.

$ protein-fasta configured "$WORK/mixed.fasta" "$WORK/mixed-uniprot.csv" \
  --rules src/protein_fasta/documents/frame_formats/uniprotkb/rules.json \
  --classifiers src/protein_fasta/documents/entry_classifiers/rules.json
Wrote 50 FASTA records and 18 columns to $WORK/mixed-uniprot.csv
$ head -n 3 "$WORK/mixed-uniprot.csv"
id,description,sequence,is_decoy,is_contaminant,is_entrapment,database,review_status,accession,entry_name,entry_mnemonic,organism_mnemonic,protein_name,organism_name,taxonomy_id,gene_name,protein_existence,sequence_version
sp|P00001|PROT00001_HUMAN,Example protein 00001 OS=Homo sapiens OX=9606 GN=GENE00001 PE=1 SV=1,MPEPTIDEKACDEFGHIKAAR,false,false,false,uniprotkb,reviewed,P00001,PROT00001_HUMAN,PROT00001,HUMAN,Example protein 00001,Homo sapiens,9606,GENE00001,1,1
ref|NP_100001.1|,Example RefSeq protein 00001 [Mus musculus],ACDEFGHIKMPEPTIDERAGK,false,false,false,,,,,,,,,,,,

Require one format for the complete database

--strict declines partial enrichment. Because the database is mixed, the result has exactly the three base columns.

$ protein-fasta configured "$WORK/mixed.fasta" "$WORK/mixed-strict.csv" \
  --rules src/protein_fasta/documents/frame_formats/uniprotkb/rules.json \
  --classifiers src/protein_fasta/documents/entry_classifiers/rules.json \
  --strict
Wrote 50 FASTA records and 3 columns to $WORK/mixed-strict.csv
$ head -n 3 "$WORK/mixed-strict.csv"
id,description,sequence
sp|P00001|PROT00001_HUMAN,Example protein 00001 OS=Homo sapiens OX=9606 GN=GENE00001 PE=1 SV=1,MPEPTIDEKACDEFGHIKAAR
ref|NP_100001.1|,Example RefSeq protein 00001 [Mus musculus],ACDEFGHIKMPEPTIDERAGK

Parse FGCZ Cont_ and decorated identifiers

The application-specific classifier marks sp|Cont_... as a contaminant without changing the identifier. A leading REV_ is removed only from the temporary parsing identifier, so the second row is both decoy and contaminant. Both rows still receive all UniProtKB metadata.

$ protein-fasta configured "$WORK/decorated.fasta" "$WORK/decorated.csv" \
  --rules src/protein_fasta/documents/frame_formats/uniprotkb/rules.json \
  --classifiers "$WORK/fgcz-classifiers.json" \
  --no-sequence
Wrote 50 FASTA records and 16 columns to $WORK/decorated.csv
$ head -n 3 "$WORK/decorated.csv"
id,description,is_decoy,is_contaminant,database,review_status,accession,entry_name,entry_mnemonic,organism_mnemonic,protein_name,organism_name,taxonomy_id,gene_name,protein_existence,sequence_version
sp|Cont_D00001|DECOR00001_HUMAN,Decorated protein 00001 OS=Homo sapiens OX=9606 GN=DECOR00001 PE=1 SV=1,false,true,uniprotkb,reviewed,Cont_D00001,DECOR00001_HUMAN,DECOR00001,HUMAN,Decorated protein 00001,Homo sapiens,9606,DECOR00001,1,1
REV_sp|Cont_D00002|DECOR00002_HUMAN,Decorated protein 00002 OS=Homo sapiens OX=9606 GN=DECOR00002 PE=1 SV=1,true,true,uniprotkb,reviewed,Cont_D00002,DECOR00002_HUMAN,DECOR00002,HUMAN,Decorated protein 00002,Homo sapiens,9606,DECOR00002,1,1

Summarize record diagnostics

The report includes record and residue counts, the complete length summary, amino-acid counts and percentages, identifier namespaces, overlapping classifications, normalization changes, and illegal sequence characters.

$ protein-fasta diagnostics "$WORK/alpha.fasta" \
  --rules src/protein_fasta/documents/diagnostics/rules.json \
  --classifiers src/protein_fasta/documents/entry_classifiers/rules.json
Records: 50; total residues: 1050
Sequence lengths: min=21; q1=21.0; median=21.0; mean=21.00; q3=21.0; max=21
Amino-acid frequencies: A=73 (6.95%), C=73 (6.95%), D=113 (10.76%), E=153 (14.57%), F=53 (5.05%), G=53 (5.05%), H=53 (5.05%), I=103 (9.81%), K=103 (9.81%), L=3 (0.29%), M=52 (4.95%), N=2 (0.19%), P=102 (9.71%), Q=2 (0.19%), R=52 (4.95%), S=2 (0.19%), T=52 (4.95%), V=2 (0.19%), W=2 (0.19%), Y=2 (0.19%)
Identifier namespaces: uniprot=50
Classifications: none
Classification combinations: unclassified=50
Normalization changes: upper-cased=0; terminal-stop-stripped=0
Illegal sequences: 0; illegal residues: none

Digest proteins

The default digestion uses packaged trypsin rules. An explicit reusable digestion policy sits beside the command that consumes it:

$ sed -n '1,60p' "$WORK/digestion.json"
{
  "enzyme": "trypsin",
  "max_length": 50,
  "min_length": 7,
  "missed_cleavages": 2
}
  • enzyme selects the separately versioned packaged cleavage expression.
  • min_length and max_length are inclusive peptide-length bounds.
  • missed_cleavages is the maximum number retained in one theoretical peptide.
$ protein-fasta digest "$WORK/alpha.fasta" "$WORK/peptides.csv" \
  --config "$WORK/digestion.json" \
  --rules src/protein_fasta/documents/enzymes/trypsin/rules.json
Wrote 255 theoretical peptides to $WORK/peptides.csv
$ head -n 3 "$WORK/peptides.csv"
protein_id,peptide,missed_cleavages
sp|P00001|PROT00001_HUMAN,MPEPTIDEK,0
sp|P00001|PROT00001_HUMAN,MPEPTIDEKACDEFGHIK,1

Compute checksums and fingerprints

checksum reports exact-file md5-file-v1 and normalized ID/sequence-content blake2b-128-id-sequence-pairs-v1. The labels are part of the result and prevent algorithm ambiguity.

$ protein-fasta checksum "$WORK/mixed.fasta"
Exact file (md5-file-v1): 78e50623e901153f50e3d6bb34010660
Normalized ID/sequence content (blake2b-128-id-sequence-pairs-v1): blake2b-128:eda072f2cf048cc3f4b5d412182cb661

Per-row hashes are optional columns on table:

$ protein-fasta table "$WORK/mixed.fasta" "$WORK/checksums.csv" \
  --strict \
  --checksums \
  --no-sequence
Wrote 50 FASTA records and 4 columns to $WORK/checksums.csv
$ head -n 3 "$WORK/checksums.csv"
id,description,sequence_hash,id_sequence_fingerprint
sp|P00001|PROT00001_HUMAN,Example protein 00001 OS=Homo sapiens OX=9606 GN=GENE00001 PE=1 SV=1,042d717628d2974630c329fdb71fc3b3,blake2b-128:bf41acf8396e01a18a22502c297d1329
ref|NP_100001.1|,Example RefSeq protein 00001 [Mus musculus],6af445164ca53b18edbd9bef8c6f971f,blake2b-128:ea9efcd3844c6e6d0c7dac89c7ab3a58

Build a database

Source preparation and biological assembly are separate commands. The first writes the canonical protein-input Parquet. The second consumes that Parquet plus the biological build parameters and writes a decoy-free FASTA and protein inventory. The result records typed artifacts, package version, checksums, counts, complete length/amino-acid summaries, normalization, and optional entrapment evidence.

Start source preparation with ordinary CLI arguments:

$ protein-fasta prepare \
  "$WORK/alpha.fasta" \
  "$WORK/protein-input.parquet" \
  --id alpha-uniprot
Wrote authored request to $WORK/protein-input.parquet.request.json
Prepared 50 protein rows from 1 sources -> $WORK/protein-input.parquet
Wrote protein-input evidence to $WORK/protein-input.parquet.result.json

The direct command wrote this authored request before reading the FASTA:

$ sed -n '1,80p' "$WORK/protein-input.parquet.request.json"
{
  "output_parquet": "protein-input.parquet",
  "schema_version": "0.1",
  "sources": [
    {
      "path": "alpha.fasta",
      "source_id": "alpha-uniprot",
      "type": "target"
    }
  ]
}
  • schema_version selects the protein-input request contract.
  • sources is the ordered input list. This direct form creates one target source.
  • source_id is the stable provenance label supplied by --id.
  • path is recorded relative to the request file.
  • output_parquet names the canonical prepared protein table, also relative to the request.

The saved request can be replayed while redirecting only its output:

$ protein-fasta prepare \
  --request "$WORK/protein-input.parquet.request.json" \
  --output "$WORK/replay/protein-input.parquet"
Prepared 50 protein rows from 1 sources -> $WORK/replay/protein-input.parquet
Wrote protein-input evidence to $WORK/replay/protein-input.parquet.result.json

Now start the biological build directly:

$ protein-fasta build "$WORK/protein-input.parquet" \
  --output "$WORK/out" \
  --project 1 \
  --dbn 1 \
  --description walkthrough \
  --date 2026-08-27
Wrote authored request to $WORK/out/build.request.json
built p1_db1_walkthrough_20260827.fasta: 50 target + 0 contaminant + 0 entrapment = 51 entries
Built 51 entries -> $WORK/out/p1_db1_walkthrough_20260827.fasta
Effective request -> $WORK/out/p1_db1_walkthrough_20260827.fasta.effective.json
Result -> $WORK/out/p1_db1_walkthrough_20260827.fasta.result.json
$ sed -n '1,100p' "$WORK/out/build.request.json"
{
  "annotation": "",
  "date": "2026-08-27",
  "name_fields": {
    "dbn": 1,
    "description": "walkthrough",
    "project": 1
  },
  "output_dir": ".",
  "schema_version": "0.3"
}
  • schema_version selects the biological-build request contract.
  • output_dir is . because the request lives inside the selected output directory.
  • date is the reproducible build date used in the FASTA name and metadata.
  • name_fields supplies project, dbn, and description to the packaged naming template.
  • annotation is the optional free-text database annotation; the direct form records it empty.

This authored request contains intent. The sibling *.effective.json records the fully resolved naming and metadata policy, while *.result.json records observed artifacts, checksums, counts, summaries, and warnings.

$ protein-fasta build "$WORK/protein-input.parquet" \
  --request "$WORK/out/build.request.json" \
  --output "$WORK/replay-build"
built p1_db1_walkthrough_20260827.fasta: 50 target + 0 contaminant + 0 entrapment = 51 entries
Built 51 entries -> $WORK/replay-build/p1_db1_walkthrough_20260827.fasta
Effective request -> $WORK/replay-build/p1_db1_walkthrough_20260827.fasta.effective.json
Result -> $WORK/replay-build/p1_db1_walkthrough_20260827.fasta.result.json
$ head -n 3 "$WORK/out/p1_db1_walkthrough_20260827.fasta"
>aa|p1_db1_walkthrough|2026-08-27, generated w protein-fasta and installed by , fgcz
CRAPCRAPCRAP
>sp|P00001|PROT00001_HUMAN Example protein 00001 OS=Homo sapiens OX=9606 GN=GENE00001 PE=1 SV=1

Generate the search database

Decoy generation is a subsequent operation over the biological inventory. It does not repeat source preparation or biological assembly.

$ protein-fasta decoy \
  "$WORK/out/p1_db1_walkthrough_20260827.fasta.protein-inventory.parquet" \
  --output "$WORK/out/p1_db1_walkthrough_d_20260827.fasta" \
  --method reverse
Wrote authored request to $WORK/out/p1_db1_walkthrough_d_20260827.fasta.request.json
Generated 50 decoys -> $WORK/out/p1_db1_walkthrough_d_20260827.fasta
Wrote decoy-generation evidence to $WORK/out/p1_db1_walkthrough_d_20260827.fasta.result.json
$ sed -n '1,80p' "$WORK/out/p1_db1_walkthrough_d_20260827.fasta.request.json"
{
  "decoy_prefix": "REV_",
  "output_fasta": "p1_db1_walkthrough_d_20260827.fasta",
  "schema_version": "0.1",
  "strategy": {
    "type": "reverse"
  }
}
  • schema_version selects the decoy request contract.
  • output_fasta is the search FASTA path relative to the request.
  • decoy_prefix records the generated identifier decoration.
  • strategy.type records the selected reverse algorithm. Direct mode also offers shuffle; DecoyPYrat and custom strategy details are advanced request-JSON variants.
$ protein-fasta decoy \
  "$WORK/out/p1_db1_walkthrough_20260827.fasta.protein-inventory.parquet" \
  --request "$WORK/out/p1_db1_walkthrough_d_20260827.fasta.request.json" \
  --output "$WORK/replay-search.fasta"
Generated 50 decoys -> $WORK/replay-search.fasta
Wrote decoy-generation evidence to $WORK/replay-search.fasta.result.json
$ head -n 3 "$WORK/out/p1_db1_walkthrough_d_20260827.fasta"
>aa|p1_db1_walkthrough|2026-08-27, generated w protein-fasta and installed by , fgcz; decoys reverse
CRAPCRAPCRAP
>sp|P00001|PROT00001_HUMAN Example protein 00001 OS=Homo sapiens OX=9606 GN=GENE00001 PE=1 SV=1

Index the example collection with SQLite

index can incrementally update an existing registry. --full rebuilds it atomically, --force revisits unchanged files, --prune removes missing sources, and --recursive includes supported FASTA files in subdirectories.

$ protein-fasta index "$WORK/registry-input" "$WORK/registry.sqlite3"
bulk load: discovered 2 FASTA files; 2 eligible below 5 GiB; 0 skipped
bulk load: file 1/2 started: alpha.fasta
bulk load: file 1/2 scan complete: 50 entries, 0.01 MB; aggregating statistics (0.0s)
bulk load: file 1/2 complete: alpha.fasta (50 entries, full, 0.0s)
bulk load: 1/2 files, 1 accepted, 0 rejected, 50 entries (0.0s)
bulk load: file 2/2 started: beta.fasta
bulk load: file 2/2 scan complete: 50 entries, 0.01 MB; aggregating statistics (0.0s)
bulk load: file 2/2 complete: beta.fasta (50 entries, full, 0.0s)
bulk load: 2/2 files, 2 accepted, 0 rejected, 100 entries (0.0s)
bulk indexes: creating entry lookup indexes
bulk indexes: entry indexes ready (0.0s)
pair statistics: materializing all database pairs
pair statistics (target): 1/1 unique-content comparisons (100.0%, 0.0s)
pair statistics (target): 2 databases collapse to 2 unique content set(s)
pair statistics (contaminant): 2 databases collapse to 1 unique content set(s)
pair statistics: wrote 2/2 materialized rows (0.0s)
pair statistics: complete (0.0s)
registry validation: integrity ok; 2 databases (2 full-detail, 0 metadata-only), 100 entries scanned, 100 details stored, 2 pair rows (0.0s)
published registry: $WORK/registry.sqlite3 (0.00 GB, 0.0s total)
Indexed 2 databases into $WORK/registry.sqlite3; rejected 0 files

Index the example collection with DuckDB

DuckDB selection is reusable registry policy, not a generated per-run request:

$ sed -n '1,60p' "$WORK/registry-duckdb.json"
{
  "schema_version": "0.1",
  "registry": {
    "backend": "duckdb"
  }
}
  • schema_version selects the registry-policy contract.
  • registry.backend selects DuckDB when creating the registry and must agree with .duckdb.

The same policy file is supplied to indexing, canonical-inventory indexing, and candidate review:

$ protein-fasta index "$WORK/registry-input" "$WORK/registry.duckdb" \
  --config "$WORK/registry-duckdb.json"
bulk load: discovered 2 FASTA files; 2 eligible below 5 GiB; 0 skipped
bulk load: file 1/2 started: alpha.fasta
bulk load: file 1/2 scan complete: 50 entries, 0.01 MB; aggregating statistics (0.1s)
bulk load: file 1/2 complete: alpha.fasta (50 entries, full, 0.2s)
bulk load: 1/2 files, 1 accepted, 0 rejected, 50 entries (0.2s)
bulk load: file 2/2 started: beta.fasta
bulk load: file 2/2 scan complete: 50 entries, 0.01 MB; aggregating statistics (0.0s)
bulk load: file 2/2 complete: beta.fasta (50 entries, full, 0.1s)
bulk load: 2/2 files, 2 accepted, 0 rejected, 100 entries (0.3s)
bulk indexes: creating entry lookup indexes
bulk indexes: entry indexes ready (0.0s)
pair statistics: materializing all database pairs
pair statistics (target): 1/1 unique-content comparisons (100.0%, 0.0s)
pair statistics (target): 2 databases collapse to 2 unique content set(s)
pair statistics (contaminant): 2 databases collapse to 1 unique content set(s)
pair statistics: wrote 2/2 materialized rows (0.0s)
pair statistics: complete (0.0s)
registry validation: integrity ok; 2 databases (2 full-detail, 0 metadata-only), 100 entries scanned, 100 details stored, 2 pair rows (0.0s)
published registry: $WORK/registry.duckdb (0.00 GB, 0.4s total)
Indexed 2 databases into $WORK/registry.duckdb; rejected 0 files

Index a canonical inventory directly

The preferred handoff indexes the accepted protein or search inventory without reparsing FASTA.

$ protein-fasta index-inventory \
  "$WORK/out/p1_db1_walkthrough_d_20260827.fasta.search-inventory.parquet" \
  "$WORK/inventory-registry.duckdb" \
  --config "$WORK/registry-duckdb.json"
indexed inventory p1_db1_walkthrough_d_20260827.fasta (101 entries, full)
Indexed 101 entries from $WORK/out/p1_db1_walkthrough_d_20260827.fasta.search-inventory.parquet

Review a candidate without registering it

$ protein-fasta candidate \
  "$WORK/out/p1_db1_walkthrough_d_20260827.fasta.search-inventory.parquet" \
  "$WORK/registry.duckdb" \
  --output "$WORK/out/candidate-comparisons.parquet" \
  --limit 10 \
  --config "$WORK/registry-duckdb.json"
Wrote authored request to $WORK/out/candidate-comparisons.parquet.request.json
Compared candidate with 2 full-detail databases -> $WORK/out/candidate-comparisons.parquet
Wrote candidate-review evidence to $WORK/out/candidate-comparisons.parquet.result.json
$ sed -n '1,80p' "$WORK/out/candidate-comparisons.parquet.request.json"
{
  "clustering_metric": "target_ids",
  "neighbour_limit": 10,
  "output_parquet": "candidate-comparisons.parquet",
  "overlap_threshold": 0.99,
  "schema_version": "0.1"
}
  • schema_version selects the candidate-review request contract.
  • output_parquet names the comparison table relative to the request.
  • overlap_threshold controls containment relationship labels.
  • clustering_metric selects target-ID or target-sequence neighbourhood distances.
  • neighbour_limit records the direct --limit value.
$ protein-fasta candidate \
  "$WORK/out/p1_db1_walkthrough_d_20260827.fasta.search-inventory.parquet" \
  "$WORK/registry.duckdb" \
  --request "$WORK/out/candidate-comparisons.parquet.request.json" \
  --output "$WORK/out/replayed-candidate.parquet" \
  --config "$WORK/registry-duckdb.json"
Compared candidate with 2 full-detail databases -> $WORK/out/replayed-candidate.parquet
Wrote candidate-review evidence to $WORK/out/replayed-candidate.parquet.result.json

Derive clean source rows from a search database

The derived-input operation retains target and contaminant proteins and explicitly counts prior sentinel, section-marker, entrapment, and decoy rows that it excludes.

$ protein-fasta derive-input \
  "$WORK/out/p1_db1_walkthrough_d_20260827.fasta.search-inventory.parquet" \
  "$WORK/out/derived-protein-input.parquet" \
  --id walkthrough-search-database
Wrote authored request to $WORK/out/derived-protein-input.parquet.request.json
Derived 50 target, 0 contaminant, and 0 foreign rows -> $WORK/out/derived-protein-input.parquet
Wrote derived protein-input evidence to $WORK/out/derived-protein-input.parquet.result.json
$ sed -n '1,80p' "$WORK/out/derived-protein-input.parquet.request.json"
{
  "output_parquet": "derived-protein-input.parquet",
  "schema_version": "0.1",
  "source_id": "walkthrough-search-database",
  "source_inventory": "p1_db1_walkthrough_d_20260827.fasta.search-inventory.parquet"
}
  • schema_version selects the derived-input request contract.
  • source_inventory identifies the existing biological or search inventory relative to the request.
  • source_id is the stable provenance label supplied by --id.
  • output_parquet names the filtered protein-input table relative to the request.
$ protein-fasta derive-input \
  --request "$WORK/out/derived-protein-input.parquet.request.json" \
  --output "$WORK/out/replayed-derived-input.parquet"
Derived 50 target, 0 contaminant, and 0 foreign rows -> $WORK/out/replayed-derived-input.parquet
Wrote derived protein-input evidence to $WORK/out/replayed-derived-input.parquet.result.json

Build canonical peptide artifacts

peptides writes canonical peptide and protein-mapping Parquet plus a unique-peptide FASTA. Memory, SQLite, and DuckDB executors return the same result contract.

$ protein-fasta peptides \
  "$WORK/out/p1_db1_walkthrough_d_20260827.fasta.search-inventory.parquet" \
  --output "$WORK/out" \
  --enzyme trypsin \
  --minimum 7 \
  --maximum 50 \
  --missed 2 \
  --partition 25
Wrote authored request to $WORK/out/peptides.request.json
Built 255 unique peptides and 553 mappings with memory -> $WORK/out/peptides.parquet
Wrote peptide-build evidence to $WORK/out/peptides.parquet.result.json
$ sed -n '1,120p' "$WORK/out/peptides.request.json"
{
  "digestion": {
    "enzyme": "trypsin",
    "max_length": 50,
    "min_length": 7,
    "missed_cleavages": 2
  },
  "execution": {
    "partition_size": 25,
    "type": "memory",
    "workers": 1
  },
  "mapping_parquet": "protein-peptide-map.parquet",
  "peptide_fasta": "peptides.fasta",
  "peptides_parquet": "peptides.parquet",
  "schema_version": "0.1"
}
  • schema_version selects the peptide-build request contract.
  • peptides_parquet, mapping_parquet, and peptide_fasta name the three canonical products relative to the request.
  • digestion records the enzyme, inclusive length limits, and maximum missed cleavages.
  • execution.type selects the in-memory executor; workers and partition_size record its deterministic execution settings. SQLite and DuckDB execution are advanced JSON variants.
$ protein-fasta peptides \
  "$WORK/out/p1_db1_walkthrough_d_20260827.fasta.search-inventory.parquet" \
  --request "$WORK/out/peptides.request.json" \
  --output "$WORK/replayed-peptides"
Built 255 unique peptides and 553 mappings with memory -> $WORK/replayed-peptides/peptides.parquet
Wrote peptide-build evidence to $WORK/replayed-peptides/peptides.parquet.result.json
$ head -n 3 "$WORK/out/peptides.fasta"
>pep|9dad5f202c8e2141d29c84f128ae0be2 mappings=1
AAKIHGFEDCAK
>pep|fcac879ee2cf977796cb51fdaeaf3520 mappings=1

Compare peptide inventories

$ protein-fasta pepcompare \
  "$WORK/out/peptides.parquet" \
  "$WORK/out/peptides.parquet" \
  --output "$WORK/out/peptide-comparison.parquet"
Wrote authored request to $WORK/out/peptide-comparison.parquet.request.json
Wrote peptide-comparison evidence to $WORK/out/peptide-comparison.parquet.result.json
$ sed -n '1,60p' "$WORK/out/peptide-comparison.parquet.request.json"
{
  "output_parquet": "peptide-comparison.parquet",
  "schema_version": "0.1"
}
  • schema_version selects the peptide-comparison request contract.
  • output_parquet names the exact population-overlap table relative to the request.
$ protein-fasta pepcompare \
  "$WORK/out/peptides.parquet" \
  "$WORK/out/peptides.parquet" \
  --request "$WORK/out/peptide-comparison.parquet.request.json" \
  --output "$WORK/out/replayed-peptide-comparison.parquet"
Wrote peptide-comparison evidence to $WORK/out/replayed-peptide-comparison.parquet.result.json

Compare decoy methods

$ protein-fasta decoy-report \
  "$WORK/out/p1_db1_walkthrough_20260827.fasta.protein-inventory.parquet" \
  --output "$WORK/out/decoy-methods.parquet" \
  --method reverse \
  --enzyme trypsin \
  --minimum 7 \
  --maximum 50 \
  --missed 2
Wrote authored request to $WORK/out/decoy-methods.parquet.request.json
Compared 1 decoy methods -> $WORK/out/decoy-methods.parquet
Wrote decoy-method evidence to $WORK/out/decoy-methods.parquet.result.json
$ sed -n '1,120p' "$WORK/out/decoy-methods.parquet.request.json"
{
  "decoy_prefix": "REV_",
  "digestion": {
    "enzyme": "trypsin",
    "max_length": 50,
    "min_length": 7,
    "missed_cleavages": 2
  },
  "output_parquet": "decoy-methods.parquet",
  "schema_version": "0.1",
  "strategies": [
    {
      "type": "reverse"
    }
  ]
}
  • schema_version selects the decoy-report request contract.
  • output_parquet names the comparison table relative to the request.
  • decoy_prefix records the decoration shared by the compared methods.
  • digestion records the enzyme and peptide limits used to compare target and decoy populations.
  • strategies is the ordered, non-repeating set of methods; reverse carries no ignored seed.
$ protein-fasta decoy-report \
  "$WORK/out/p1_db1_walkthrough_20260827.fasta.protein-inventory.parquet" \
  --request "$WORK/out/decoy-methods.parquet.request.json" \
  --output "$WORK/out/replayed-decoy-methods.parquet"
Compared 1 decoy methods -> $WORK/out/replayed-decoy-methods.parquet
Wrote decoy-method evidence to $WORK/out/replayed-decoy-methods.parquet.result.json

Export the SQLite registry summary

$ protein-fasta registry "$WORK/registry.sqlite3" "$WORK/registry-sqlite.csv"
Wrote 2 registry rows to $WORK/registry-sqlite.csv
$ head -n 3 "$WORK/registry-sqlite.csv"
database_id,relative_path,filename,database_name,detail_level,entries,targets,decoys,contaminants,entrapments,sentinels,distinct_target_ids,distinct_target_sequences,target_id_fingerprint,target_description_fingerprint,target_content_fingerprint,total_residues,indexed_at
1,alpha.fasta,alpha.fasta,alpha,full,50,50,0,0,0,0,50,50,blake2b-128:a54e4c772fa6d02bbbc5efcc9f60ff3b,blake2b-128:f438fd15908c3ad2ae0ef6f5971c5583,blake2b-128:38fbf3b899fc2599c1e5f582209a9dfa,1050,2026-10-05T05:33:29.314081+00:00
2,beta.fasta,beta.fasta,beta,full,50,50,0,0,0,0,50,50,blake2b-128:fdaef5fd1198608194cd3a12a8952209,blake2b-128:5caf905d856f68e642874d6c6d894dc7,blake2b-128:5e6a316cf5d068e818dacc5e7e1f0471,1050,2026-10-05T05:33:29.316332+00:00

Export the DuckDB registry summary

$ protein-fasta registry "$WORK/registry.duckdb" "$WORK/registry-duckdb.csv"
Wrote 2 registry rows to $WORK/registry-duckdb.csv
$ head -n 3 "$WORK/registry-duckdb.csv"
database_id,relative_path,filename,database_name,detail_level,entries,targets,decoys,contaminants,entrapments,sentinels,distinct_target_ids,distinct_target_sequences,target_id_fingerprint,target_description_fingerprint,target_content_fingerprint,total_residues,indexed_at
1,alpha.fasta,alpha.fasta,alpha,full,50,50,0,0,0,0,50,50,blake2b-128:a54e4c772fa6d02bbbc5efcc9f60ff3b,blake2b-128:f438fd15908c3ad2ae0ef6f5971c5583,blake2b-128:38fbf3b899fc2599c1e5f582209a9dfa,1050,2026-10-05T05:33:30.012676+00:00
2,beta.fasta,beta.fasta,beta,full,50,50,0,0,0,0,50,50,blake2b-128:fdaef5fd1198608194cd3a12a8952209,blake2b-128:5caf905d856f68e642874d6c6d894dc7,blake2b-128:5e6a316cf5d068e818dacc5e7e1f0471,1050,2026-10-05T05:33:30.088330+00:00

Compare one database

Database identifiers are stable for this path-sorted two-file fixture, so database 1 is selected. The export contains directional coverage, containment, Jaccard, exact-pair, changed-ID, and relationship evidence.

$ protein-fasta compare "$WORK/registry.duckdb" 1 "$WORK/comparison.csv"
Wrote 1 database comparisons to $WORK/comparison.csv
$ head -n 3 "$WORK/comparison.csv"
database_id,filename,kind,relationship,selected_ids,other_ids,shared_ids,id_jaccard,containment,shared_sequences,sequence_jaccard,shared_descriptions,description_jaccard,shared_exact_pairs,changed_shared_ids,exact_content
2,beta.fasta,target,partial_overlap,50,50,25,0.3333333333333333,0.5,25,0.3333333333333333,25,0.3333333333333333,25,0,false

Export materialized pair metrics

pairs writes long target/contaminant pair metrics and can additionally write symmetric target-ID and target-sequence matrices.

$ protein-fasta pairs "$WORK/registry.duckdb" "$WORK/pairs.tsv" \
  --ids "$WORK/id-matrix.tsv" \
  --sequences "$WORK/sequence-matrix.tsv"
Wrote 1 pair rows to $WORK/pairs.tsv
$ head -n 3 "$WORK/pairs.tsv"
database_a_relative_path    database_b_relative_path    target_entries_a    target_entries_b    target_distinct_ids_a   target_distinct_ids_b   target_shared_ids   target_id_jaccard   target_id_coverage_a_by_b   target_id_coverage_b_by_a   target_id_containment   target_a_contained_in_b target_b_contained_in_a target_exact_id_set target_distinct_sequences_a target_distinct_sequences_b target_shared_sequences target_sequence_jaccard target_sequence_coverage_a_by_b target_sequence_coverage_b_by_a target_sequence_containment target_sequence_a_contained_in_b    target_sequence_b_contained_in_a    target_exact_sequence_set   target_distinct_descriptions_a  target_distinct_descriptions_b  target_shared_descriptions  target_description_jaccard  target_description_coverage_a_by_b  target_description_coverage_b_by_a  target_description_containment  target_description_a_contained_in_b target_description_b_contained_in_a target_exact_description_set    target_distinct_pairs_a target_distinct_pairs_b target_shared_exact_pairs   target_matching_shared_ids  target_changed_shared_ids   target_exact_content    contaminant_entries_a   contaminant_entries_b   contaminant_distinct_ids_a  contaminant_distinct_ids_b  contaminant_shared_ids  contaminant_id_jaccard  contaminant_id_coverage_a_by_b  contaminant_id_coverage_b_by_a  contaminant_id_containment  contaminant_a_contained_in_b    contaminant_b_contained_in_a    contaminant_distinct_sequences_a    contaminant_distinct_sequences_b    contaminant_shared_sequences    contaminant_sequence_jaccard    contaminant_sequence_coverage_a_by_b    contaminant_sequence_coverage_b_by_a    contaminant_sequence_containment    contaminant_sequence_a_contained_in_b   contaminant_sequence_b_contained_in_a   contaminant_exact_sequence_set  contaminant_distinct_descriptions_a contaminant_distinct_descriptions_b contaminant_shared_descriptions contaminant_description_jaccard contaminant_description_coverage_a_by_b contaminant_description_coverage_b_by_a contaminant_description_containment contaminant_description_a_contained_in_b    contaminant_description_b_contained_in_a    contaminant_exact_description_set   contaminant_distinct_pairs_a    contaminant_distinct_pairs_b    contaminant_shared_exact_pairs  contaminant_matching_shared_ids contaminant_changed_shared_ids  contaminant_exact_id_set    contaminant_exact_content
alpha.fasta beta.fasta  50  50  50  50  25  0.333333333333  0.5 0.5 0.5 false   false   false   50  50  25  0.333333333333  0.5 0.5 0.5 false   false   false   50  50  25  0.333333333333  0.5 0.5 0.5 false   false   false   50  50  25  25  0   false   0   0   0   0   0   0.0 0.0 0.0 0.0 false   false   0   0   0   0.0 0.0 0.0 0.0 false   false   false   0   0   0   0.0 0.0 0.0 0.0 false   false   false   0   0   0   0   0   false   false

Cluster registered databases

cluster performs deterministic average-linkage clustering and logs the leaf order. Use --metric target-sequences to cluster on target sequence Jaccard instead of the default target-ID Jaccard.

$ protein-fasta cluster "$WORK/registry.duckdb" "$WORK/clusters.csv"
Wrote 1 cluster merges to $WORK/clusters.csv
Leaf order: alpha.fasta, beta.fasta
$ head -n 3 "$WORK/clusters.csv"
metric,cluster_id,left_id,right_id,distance,leaf_count
target_ids,2,0,1,0.6666666666666667,2