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-sequenceapplies 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.jsonbefore computation.--request PATHreplays it;--outputmay redirect replayed products without changing policy. - Authored requests are never overwritten. Use
--save PATHto choose their first-run location. preparewrites the canonical protein-input Parquet.buildconsumes it and produces only a decoy-free biological FASTA and protein inventory.decoyis 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.candidateis read-only.index-inventoryis 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_versionselects the validated catalog-request contract.output_diris relative to the request file, so.means itscatalog/directory.selection.typerequests the UniProt reference-proteome catalogue.timeout_secondsrecords 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; oropg— 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.typesays the source is an explicit proteome rather than a taxonomy search.selection.proteome_idrecords the chosen proteome.acquisition.typerecords the canonical-per-gene behavior selected byopg.output_fastais the FASTA path relative to the request.timeout_secondsandschema_versionhave 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
}
enzymeselects the separately versioned packaged cleavage expression.min_lengthandmax_lengthare inclusive peptide-length bounds.missed_cleavagesis 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_versionselects the protein-input request contract.sourcesis the ordered input list. This direct form creates onetargetsource.source_idis the stable provenance label supplied by--id.pathis recorded relative to the request file.output_parquetnames 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_versionselects the biological-build request contract.output_diris.because the request lives inside the selected output directory.dateis the reproducible build date used in the FASTA name and metadata.name_fieldssuppliesproject,dbn, anddescriptionto the packaged naming template.annotationis 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_versionselects the decoy request contract.output_fastais the search FASTA path relative to the request.decoy_prefixrecords the generated identifier decoration.strategy.typerecords 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_versionselects the registry-policy contract.registry.backendselects 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_versionselects the candidate-review request contract.output_parquetnames the comparison table relative to the request.overlap_thresholdcontrols containment relationship labels.clustering_metricselects target-ID or target-sequence neighbourhood distances.neighbour_limitrecords the direct--limitvalue.
$ 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_versionselects the derived-input request contract.source_inventoryidentifies the existing biological or search inventory relative to the request.source_idis the stable provenance label supplied by--id.output_parquetnames 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_versionselects the peptide-build request contract.peptides_parquet,mapping_parquet, andpeptide_fastaname the three canonical products relative to the request.digestionrecords the enzyme, inclusive length limits, and maximum missed cleavages.execution.typeselects the in-memory executor;workersandpartition_sizerecord 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_versionselects the peptide-comparison request contract.output_parquetnames 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_versionselects the decoy-report request contract.output_parquetnames the comparison table relative to the request.decoy_prefixrecords the decoration shared by the compared methods.digestionrecords the enzyme and peptide limits used to compare target and decoy populations.strategiesis 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