Man Page Reference¶
The full man-page reference for octa(1). The source of truth is
docs/cli/octa.1.adoc
(AsciiDoc); this page mirrors that content as Markdown for the
docs site.
On Linux (with the man page installed) man octa gives you the
same content at a terminal. See
Installation for how to get
the man page on disk.
Name¶
octa: multi-format tabular data viewer, editor, CLI tool, and MCP server.
Synopsis¶
octa [FILE...]
octa --schema FILE [-f FORMAT] [--rows N|all]
octa --head FILE [-n N] [--sort-by COLS [--sort-desc COLS] [--natural]] [-f FORMAT] [--rows N|all]
octa --tail FILE [-n N] [-f FORMAT] [--rows N|all]
octa --sample FILE [-n N] [--seed N] [-f FORMAT] [--rows N|all]
octa --convert IN OUT [--sort-by COLS [--sort-desc COLS] [--natural]] [--rows N|all] [--password-env VAR]
octa --sql FILE -q QUERY [--explain] [-f FORMAT] [--rows N|all]
[--sql-table NAME=PATH ...] [--sql-attach ALIAS=PATH ...]
[--sql-write-to PATH --sql-write-table TABLE
[--sql-write-schema SCHEMA] [--sql-write-mode create|append|replace]]
octa --export-schema FILE [-t TARGET]
octa --compare-schemas FILE_A FILE_B [--table-a NAME] [--table-b NAME] [-f FORMAT]
octa --diff FILE_A FILE_B [--diff-mode MODE] [--diff-on COLS] [-f FORMAT]
[--diff-tolerance N] [--diff-tolerance-pct P] [--diff-time-tolerance SECS]
[--diff-ignore-case] [--diff-ignore-spaces] [--diff-ignore-columns COLS]
octa --diff FILE --diff-db CONN --diff-db-table TABLE [--diff-mode MODE]
[--diff-on COLS] [-f FORMAT]
[--diff-tolerance N] [--diff-tolerance-pct P] [--diff-time-tolerance SECS]
[--diff-ignore-case] [--diff-ignore-spaces] [--diff-ignore-columns COLS]
octa --describe FILE [--table NAME] [--sample-rows N] [--deep] [-f FORMAT]
octa --validate-schema FILE --expect-schema SCHEMA_FILE [--table NAME] [-f FORMAT]
octa --schema-drift DIR [--recursive] [--ignore-case] [-f FORMAT]
octa --drift-report FILE_A FILE_B [--fail-on SPEC] [-f FORMAT]
octa --recipe RECIPE FILE [--recipe-out FILE] [-f FORMAT]
octa --overlaps FILE [--overlaps-start COL] [--overlaps-end COL] [--overlaps-lane COL]
[--overlaps-label COL] [-f FORMAT]
octa --shapes FILE --shapes-column COL [-f FORMAT]
octa --lookups FILE [--lookups-min N] [-f FORMAT]
octa --cell-history FILE --history-column COL (--history-key COLS --history-value VALUES | --history-row N) [--history-depth N] [-f FORMAT]
octa --spatial-join FILE --spatial-layer FILE... [--spatial-op inside|nearest] [--within-km N] [-f FORMAT]
octa --forecast FILE --forecast-x COL --forecast-y COL [--forecast-periods N] [--forecast-season N] [-f FORMAT]
octa --test-data FILE... [--test-data-rows N] [--seed N] [--test-data-out PATH]
[--test-data-rename-categories] [-f FORMAT]
octa --merge FILE FILE... [--merge-original FILE] [--merge-key COLS]
[--merge-prefer N] [--merge-out FILE] [--merge-format EXT] [-f FORMAT]
octa --check FILE --rules RULES.toml [-f FORMAT]
octa --relationships DIR [--recursive] [-f FORMAT]
octa --compare-folders FOLDER FOLDER... [--no-recursive] [-f FORMAT]
octa --harmonise-schema DIR --out-dir DIR [--target-file FILE]
[--recursive] [--ignore-case] [--overwrite]
octa --report OUT.html FILE [--report-sample N] [--report-sections LIST]
octa --unique-columns FILE [--table NAME] [--max-combo N] [-f FORMAT]
octa --anonymize SPEC FILE [-f FORMAT]
octa --dedupe FILE [--dedupe-on COLS] [--dedupe-keep WHICH] [-f FORMAT]
octa --impute COL=STRATEGY FILE [-f FORMAT]
octa --outliers FILE [--outlier-method M] [--outlier-cols COLS] [--outlier-k K] [-f FORMAT]
octa --detect-pii FILE [--pii-sample N] [-f FORMAT]
octa --union FILE --union-file FILE [--union-drop COL] [--union-cast COL=TYPE]
[--union-ignore-case] [-f FORMAT]
octa --join FILE --join-file FILE --join-on COLS [--join-type TYPE] [-f FORMAT]
octa --fuzzy-join FILE --fuzzy-join-file FILE --fuzzy-on LEFT=RIGHT
[--fuzzy-method NAME] [--fuzzy-threshold N] [--fuzzy-block LEFT=RIGHT]
[--fuzzy-join-type TYPE] [--fuzzy-max-rows N] [-f FORMAT]
octa --partition-by COL --out-dir DIR FILE [--partition-format EXT]
octa --split-rows N | --split-size SIZE --out-dir DIR FILE [--partition-by COL]
octa --db-tables --db CONNECTION [--db-catalog NAME] [-f FORMAT]
octa --db-query SQL --db CONNECTION [--explain] [-f FORMAT]
octa --db-write-table SCHEMA.TABLE --db CONNECTION FILE [--db-write-mode MODE] [--db-catalog NAME]
octa --db-copy SCHEMA.TABLE --db CONNECTION --db-copy-to CONNECTION [--db-copy-target SCHEMA.TABLE] [--db-write-mode MODE]
octa --db-export SCHEMA --db CONNECTION --out-dir DIR [--to EXT] [--db-export-tables T1,T2] [--overwrite]
octa --mcp
Description¶
octa is a desktop application for viewing and editing tabular data files. It opens Parquet, CSV, JSON, SQLite, DuckDB, Excel, and roughly twenty more formats in a spreadsheet-like view with sorting, filtering, full-text search, inline editing, SQL queries, and file comparison.
When invoked with no flags, it launches the graphical interface,
optionally opening the supplied FILE(s) in tabs. When invoked
with one of the action flags (--schema, --head, --tail,
--sample, --convert, --sql, --export-schema,
--compare-schemas, --diff, --describe, --validate-schema,
--schema-drift, --drift-report, --merge, --recipe, --test-data, --overlaps, --shapes, --lookups, --cell-history, --spatial-join, --forecast, --check, --relationships, --compare-folders,
--harmonise-schema, --report, --fuzzy-join,
--unique-columns, --anonymize, --dedupe, --impute,
--outliers, --detect-pii, --union, --join, --partition-by,
--batch-convert, --resample, --rolling, --mcp), it performs
that action and exits.
Action flags are mutually exclusive. Trailing FILE arguments are ignored (with a warning) when an action flag is set.
A FILE argument may also be a cloud object URL (s3://bucket/key,
az://container/blob, gs://bucket/key, sftp://user@host/path,
davs://host/path). The object is downloaded to
a temporary file and read as usual. Credentials come from a saved cloud
connection covering the URL, otherwise from the ambient chain (AWS_*
environment variables, a cached SSO session, az login, Google
application default credentials). Cloud objects are read-only here:
output still goes to a local path.
Action Flags¶
--schema FILE- Print the column schema of FILE as a two-column table
(column name, data type). For streaming formats (Parquet, CSV,
TSV) the reader loads the initial-row batch (2,000,000 rows
by default) and projects the schema from that. See
octa --schemafor the dedicated page. --head FILE- Print the first N rows of FILE to standard output. N
defaults to 20 and is set with
-n/--lines. For streaming formats, the reader stops at the initial-load cap and N is a slice off that. Seeocta --head. --tail FILE- Print the last N rows of FILE. N defaults to 20 (
-n/--lines). Streaming formats load with the initial-row cap, so the tail reflects the end of the loaded window; raise--rowsto tail the true end of a very large file. --sample FILE- Print a random N-row sample of FILE (without replacement,
original row order preserved). N defaults to 20 (
-n/--lines); the sample is reproducible for a given--seed(default 0). --convert IN OUT- Convert IN to OUT. Both formats are inferred from each
path's extension and routed through the shared format registry.
Read-only output formats (SAS, R datasets, HDF5, NetCDF, EPUB,
GeoJSON) are rejected with a clear error. Conversion is bounded
by the initial-load cap (2 M rows by default); pass
--rows allto convert the full file. Seeocta --convert. --sql FILE- Run a SQL query against FILE. The query is supplied via
-q/--query. FILE is exposed to DuckDB as a temporary table called data. Extra tables can be loaded with--sql-table NAME=PATHand whole DuckDB / SQLite databases can be attached with--sql-attach ALIAS=PATH, so a single invocation can JOIN across formats. The SELECT result can be persisted to a DuckDB or SQLite file via--sql-write-to. Mutations (INSERT / UPDATE / DELETE) ondataitself do not persist back to FILE, because the in-memory DuckDB connection is discarded at exit. Seeocta --sql. --export-schema FILE- Render FILE's column schema as SQL DDL, a Pydantic model, a
TypeScript interface, a JSON Schema document, or a Rust struct,
and print it to standard output. The target is chosen with
-t/--target(defaultpostgres); only the column list is read. Seeocta --export-schema. --compare-schemas FILE_A FILE_B- Diff the column schemas of two files. Prints a four-column
table (
status/column/type_a/type_b). Matching is by exact, case-sensitive column name. Use--table-a/--table-bto pick a specific table on multi-table sources. Seeocta --compare-schemas. --diff FILE_A FILE_B- Compare two files.
--diff-modeselects the strategy; the output is a table tagged by a leadingstatuscolumn, and a summary line (per-mode counts) goes to standard error. Complements--compare-schemas, which diffs only the column metadata. --diff-mode MODE- Comparison strategy for
--diff(defaultset):setcompares rows by whole-row content (every column, positionally) and prints rows unique to each side (only_in_a/only_in_b).orderedlines up row i of A with row i of B and prints matched rows that differ aschanged(with achanged_columnscolumn) plus any trailingonly_in_a/only_in_brows.joinmatches rows on the--diff-onkey column(s) and prints added / removed / changed rows. --diff-on COLS- Key column(s) for
--diff-mode join, comma-separated (matched by name). Required when the mode isjoin; ignored otherwise. --diff-tolerance N- Numbers within this absolute difference count as the same. Ordered and join modes only; set mode refuses it.
--diff-tolerance-pct P- Numbers within this percentage of the larger value count as the same. A pair passes if it is within either number tolerance.
--diff-time-tolerance SECS- Dates and times at most this many seconds apart count as the same. A date counts as midnight.
--diff-ignore-case- Compare text without regard to upper and lower case, join keys included.
--diff-ignore-spaces- Compare text trimmed, with runs of spaces as one, join keys included.
--diff-ignore-columns COLS- Columns to leave out of the comparison, comma-separated or repeated. They still appear in the output.
--diff-db CONN- Compare against a live database table instead of a second file.
CONN names a saved database connection. With this set,
--difftakes a single positional file: that file is side A and the table is side B, soonly_in_ameans "in the file, not the table". Requires--diff-db-table. The table is read under the usual row cap. See Compare with a Database Table. --diff-db-table TABLE- Table to compare against on the
--diff-dbconnection, as SCHEMA.TABLE or CATALOG.SCHEMA.TABLE (the three-part form is for Trino, Snowflake, Databricks and BigQuery). An unqualified name uses the connection's own database. --describe FILE- Print a one-shot orientation snapshot of FILE: format, file
size, row count, column schema, and a small sample of rows.
Use
--sample-rows Nto change the preview size (default 5, max 100), and--deepto add the file's physical layout. Seeocta --describe. --validate-schema FILE- Check FILE's column schema against the JSON Schema given by
--expect-schema SCHEMA_FILE. Exit code is0on a clean match and1otherwise, which is CI-pipeable. Schemas produced by--export-schema -t json-schemaround-trip cleanly. Seeocta --validate-schema. --schema-drift DIR- Scan DIR and report which files disagree about their columns.
Files are grouped by schema, largest group first. The report
table goes to stdout; the per-variant file lists, any unreadable
files and the drifting column names go to stderr. Exit code is
0when every file agrees and1otherwise, which is CI-pipeable. Add--recursiveto walk subdirectories (depth 8) and--ignore-caseto treat names differing only in case as one column. Seeocta --schema-drift. --drift-report FILE_A FILE_B- Compare two versions of the same dataset and report how it moved.
Columns are matched by name: one present on a single side is
reported as added or removed, and every shared column gets its
null rate, distinct count and, when numeric, its minimum, maximum
and mean compared. Columns with few enough distinct values also
list the category values that appeared and vanished. Every figure
comes from the same Summary pass the Analyse -> Summary tab
uses. The report goes to stdout; the row counts, added and removed
column names and the pass/fail summary go to stderr. Exit code is
0unless--fail-onwas given and a gate was breached. Seeocta --drift-report. --fail-on SPEC- Turn
--drift-reportinto a CI gate. SPEC is a comma-separated list of metric:change pairs, for examplenull_rate:0.05,rows:0.1, where change is the largest relative move that still passes. A metric that never appears is never applied. A baseline of zero that moved at all counts as an unbounded change, so a null rate going from0to0.5breaches any gate. Metric names arerows,null_rate,distinct_count,min,maxandmean. --recipe RECIPE FILE- Replay a recipe (a
.ocpfile, short for Octa reCiPe, recorded in the GUI) on FILE. Every step runs by column name. When any step cannot run, nothing is written, the skipped steps go to stderr and the exit code is1. Seeocta --recipe. --recipe-out FILE- Write the
--reciperesult to FILE instead of stdout. --overlaps FILE- Rows whose time spans overlap inside a lane (two bookings of one
room). One row per pair on stdout; exits 1 when any is found.
Spans that only touch do not overlap. See
octa --overlaps. --overlaps-start COL,--overlaps-end COL- Start and end columns for
--overlaps(default: the first two date columns). No end makes every row a point. --overlaps-lane COL- Only rows with the same value in COL can overlap.
--overlaps-label COL- A column shown beside each row of an
--overlapspair. --shapes FILE- What a column's values look like: digits folded to
9, capitals toA, other letters toa, punctuation kept. One row per shape, most common first, with a count and an example. Requires--shapes-column. Seeocta --shapes. --shapes-column COL- Column to shape for
--shapes. --lookups FILE- Hidden lookup tables: columns that always follow another column. One
row per key and following column, with its consistency, conflicting
keys and breaking rows. See
octa --lookups. --lookups-min N- Share of rows (0 to 1) that must agree with their key for
--lookups. Default0.95. --cell-history FILE- The commits that changed one cell of a file in a Git repository,
newest first, following the row by key and the file through renames.
Needs
--history-columnand either--history-key+--history-valueor--history-row. Seeocta --cell-history. --history-column COL- Column of the cell for
--cell-history. --history-key COLS- Comma-separated key column(s) that identify the row.
--history-value VALUES- Comma-separated values of the row in each key column.
--history-row N- 1-based row number, when there is no key.
--history-depth N- How many commits to read, newest first. Default
50. --spatial-join FILE- Join by location: each point of FILE gets the columns of the layer
polygon it lies in, or of the nearest layer point with its distance.
Needs at least one
--spatial-layer. Seeocta --spatial-join. --spatial-layer FILE- A table to join against. Repeat for several layers.
--spatial-op inside|nearest- The operation. Default
inside. --within-km N- With
--spatial-op nearest: points farther than this stay empty. --forecast FILE- Forecast one column over time (Holt-Winters) with 80% and 95%
ranges. Needs
--forecast-xand--forecast-y. Seeocta --forecast. --forecast-x COL- Time column: dates, date-times or numbers, evenly spaced.
--forecast-y COL- Column of numbers to forecast.
--forecast-periods N- How many periods ahead. Default
12. --forecast-season N- Season length in points. Default: read from the spacing.
--test-data FILE...- Generate new rows shaped like each FILE: the same columns, numbers
and dates from the real spread, fake names, emails, IBANs and IDs,
empty cells at the real rate. Several files are generated together so
links between them still join. See
octa --test-data. --test-data-rows N- Rows per
--test-datatable (default: as many as the real one). --test-data-out PATH- Output file for one
--test-datainput, or an existing folder for several (written as<name>_test.<ext>). --test-data-rename-categories- Replace real category values with
value_1,value_2, ... --merge FILE FILE...- Merge two or more versions of a table, per row and per cell. With
--merge-original, a change made in one version is taken and only different changes to one cell, or a row deleted in one version and edited in another, conflict; without it, every cell where the versions differ conflicts. Rows are matched by--merge-key, or by position without one. While a conflict is open nothing is written, the conflicts are printed on stdout and the exit code is1, which makes it usable as a git merge driver. Seeocta --merge. --merge-original FILE- The table every
--mergeversion was edited from. Optional. --merge-key COLS- Key column(s) for
--merge, comma-separated or repeated. --merge-prefer N- Settle every
--mergeconflict in favour of version N (1 = the first file given to--merge). --merge-out FILE- Write the
--mergeresult to FILE instead of stdout. --merge-format EXT- Format of the
--mergefiles, for files without an extension. An extension (csv) or a file name to take it from (git's%P). --check FILE- Check FILE's values against the rules in
--rules(required) and report which rules failed. The rules file is TOML, one[[rule]]table per check, naming its column and one of the kindsnot_null,unique,range(withminandmax),regex(withpattern) ormax_length. A rule without acolumnapplies to every column. The report goes to stdout with one row per failing rule, its failure count and up to three offending values; the summary line goes to stderr. Exit code is1on any violation and on any rule that could not run, because a rules file whose columns have since been renamed would otherwise report a clean run over checks it silently skipped. The same file is written and read by the Data -> Data validation... dialog. Seeocta --check. --rules FILE- The TOML rules file for
--check. --relationships DIR- Rank the likely relationships between the tables in DIR and
print them best first: which column of which table lines up with
which column of which other table. Names take no part in the
ranking; the score comes from how much the values overlap,
weighted by how distinct each side is. Each row also carries
orphans, the number of distinct values on the left with no partner on the right, which is what separates two candidates that overlap identically. Reads values, so it is capped at 30 files and at 10,000 rows per table. Unreadable files are noted on stderr. Always exits0: this is a report, not a gate. Add--recursiveto walk subdirectories. Seeocta --relationships. --compare-folders FOLDER FOLDER...- Compare two or more folders file by file. Each FOLDER is a local
path or a cloud URL (
s3://,az://,gs://,sftp://,davs://). Files are matched by their path inside each folder; copies of different sizes are different without being read, and copies of equal size are compared by a BLAKE3 hash of their contents. Prints one row per file with astatusofsame,differentormissing, the folders that have it and lack it, and the size, modified time and hash per folder. Each folder is listed up to the file limit from the settings (folder_compare_max_files, default 100,000); a folder cut short is named on stderr. Exit code is0when every file is the same everywhere and1when anything differs, is missing, or a folder hit the limit. Seeocta --compare-folders. --no-recursive- With
--compare-folders: compare only the top level of each folder. --stream- Let DuckDB scan the file where it lies instead of loading its
rows. Applies to
--sqlover a Parquet, CSV or JSON file, where it lets an aggregate cover every row of a file far larger than memory: the primary file is registered as a view nameddatarather than as a loaded table, so--rowsno longer bounds what the query sees. Every other action needs the rows themselves and says so on stderr rather than ignoring the flag. --harmonise-schema DIR- Rewrite every file in DIR to one common set of columns, writing
harmonised copies into
--out-dir(required). The originals are never modified. The target schema is the shape most files already have, or the schema of--target-filewhen given. A column missing from a file is added as nulls; a column not in the target is dropped and named in the report. A file whose values cannot be cast to a target type is refused rather than written with nulls in place of those values, because a harmonised folder of silently emptied cells looks clean and is not. Two inputs that would write the same output name are both refused rather than one renamed. Exit code is0when nothing was refused and1otherwise. Add--recursive,--ignore-case, and--overwrite. Seeocta --harmonise-schema. --report OUT.html FILE- Write an HTML profiling report for FILE to OUT.html:
per-column statistics, distribution charts, the most common values
and a correlation matrix. The document is self-contained (inline
CSS, inline SVG, no JavaScript) and fetches nothing.
--report-sections LISTpicks a comma-separated subset ofstats,distributions,top_valuesandcorrelation(default: all four); an unknown name is an error.--report-sample Nprofiles a random sample of N rows and says so in the output. Seeocta --report. --fuzzy-join FILE- Join FILE to each
--fuzzy-join-fileon how similar the values are rather than on exact equality, for tables that name the same thing differently.--fuzzy-on LEFT=RIGHTnames the columns to compare and may be repeated (scores are averaged).--fuzzy-methodisedit_ratio(default),jaro_winklerortoken_set;--fuzzy-thresholdis the minimum average score (default0.85);--fuzzy-block LEFT=RIGHTcompares only rows agreeing exactly on those columns;--fuzzy-join-typeisinner,left(default),rightorfull;--fuzzy-max-rowscaps the rows per side (default20000). Each left row keeps its single best partner and the output gainsmatch_score_Nandambiguous_Nper step. Seeocta --fuzzy-join. --unique-columns FILE- Find columns (and optional small combinations) whose values are
unique across FILE. Useful for primary-key reconnaissance.
Use
--max-combo N(clamped to[1, 3]) to test pairs / triples. Seeocta --unique-columns. --anonymize SPEC FILE- Mask / scramble sensitive columns of FILE per the JSON SPEC
file and print the sanitised table to stdout (the input file is
never modified). The spec lists per-column rules (
hash/partial_mask/redact/fake) plus an optional sharedsalt; columns are named. The same value plus the same salt always maps the same way, so duplicates stay linked and a re-run re-joins to an earlier export. Seeocta --anonymize. --dedupe FILE- Remove duplicate rows from FILE and print the result. Without
--dedupe-onthe whole row is the key;--dedupe-keep(first/last) picks the surviving occurrence. Seeocta --dedupe. --impute COL=STRATEGY- Fill missing cells of a column of the positional FILE (repeatable).
Strategies:
mean,median,mode,ffill,bfill,const:VALUE. Seeocta --impute. --outliers FILE- Flag numeric outlier cells in FILE.
--outlier-method(iqr/zscore),--outlier-cols,--outlier-ktune the scan. Seeocta --outliers. --detect-pii FILE- Scan FILE for likely PII columns (email, phone, IBAN, credit
card, SSN).
--pii-sample Nsets the per-column sample size. Seeocta --detect-pii. --union FILE- Stack the positional FILE plus every
--union-fileinto one table.--union-ignore-casemerges column names differing only in case.--union-dropomits columns,--union-cast COL=TYPEoverrides a target type. Seeocta --union. --join FILE- Join the positional FILE(s) plus every
--join-fileon the--join-onkey(s).--join-typeisleft/inner/right/full/semi/anti/asof. Seeocta --join. --partition-by COL- Split the positional FILE into one file per distinct value of
COL, written into
--out-dir.--partition-formatsets the output extension. Seeocta --partition-by. --batch-convert- Convert every positional FILE into
--out-dir(required) as--to EXT(required). Output names are<stem>.<ext>; two inputs sharing a stem get_2,_3suffixes in input order, so a run cannot overwrite its own earlier output. Existing outputs are skipped unless--overwriteis given.--password-env VARwrites every output as its own password-protected AES-256 zip (a.csvbecomesa.csv.zip), passphrase from environment variableVAR. One failed file does not stop the run; stdout carries a headerlessinput<TAB>output<TAB>statuslisting, errors and the summary go to stderr, and the exit code is 1 when any file failed. Seeocta --batch-convert. --resample COL- Group the positional FILE into time buckets: one row per
--intervalof COL.--intervalisminute,hour,day(default),week,month,quarteroryear;--value-cols(required) names the columns to aggregate;--aggissum(default),mean,min,max,count,firstorlast;--group-byadds extra grouping columns, giving one series per combination. The time column is cast withTRY_CAST, so a row that will not parse buckets as NULL instead of failing the run. Seeocta --resample. --rolling COL- Add a rolling aggregate of COL over the previous
--window Nrows (including the current row).--order-by COLis required - a rolling aggregate over unordered rows is meaningless.--aggismean(default),sum,min,max,count,firstorlast;--partition-by-colsrestarts the frame per group (spelled that way because--partition-byis the split-into-files action). Seeocta --rolling. --db-tables- List every schema and table of the saved database connection named
by
--db CONNECTION(Settings -> Databases) as a two-column table. On Trino, Snowflake, Databricks and BigQuery, which have a catalog level above the schema, the catalogs themselves are listed unless--db-catalognames one. --explain- With
--sqlor--db-query: show the query plan instead of running the statement. Prints the steps in plain words, numbered in the order the database works through them; with-f jsonor-f csv, one row per step. One statement. With--sqlthe plan is DuckDB's own (the local workspace); with--db-queryit works on PostgreSQL, MySQL / MariaDB, Databricks and BigQuery (BigQuery: the bytes it would read). Nothing is executed. --db-query SQL- Run one SQL statement on the saved connection named by
--db, server-side and in the engine's native dialect. SELECT results print in-f FORMAT; mutations report rows affected and are refused unless the connection's "Allow writes" switch is on. Results stop at the initial-load row cap; use--rowsto raise or lift it. --db-write-table SCHEMA.TABLE- Write the positional FILE into the given server table on the
--dbconnection.--db-write-modeiscreate(default; error if the table exists),append, orreplace. Refused unless the connection allows writes. --db-catalog NAME- Catalog (the top namespace level) for a three-level engine, used by
--db-tablesand--db-write-table. Only Snowflake, Databricks and BigQuery have one; passing it to any other engine is an error. With--db-tablesand no catalog, the catalogs themselves are listed. --db-copy SCHEMA.TABLE- Copy a table from the
--dbconnection to another saved connection, server to server. Requires--db-copy-to.--db-write-modeselectscreate(default),appendorreplace. Refused unless the target connection allows writes. --db-copy-to CONNECTION- Target connection for
--db-copy, a saved connection name or id. --db-copy-target SCHEMA.TABLE- Target table for
--db-copy. Defaults to the source schema and table. --db-copy-target-catalog NAME- Target catalog for
--db-copyon a three-level engine. --db-export SCHEMA- Export the tables of SCHEMA in the
--dbconnection into--out-dir, one file per table named after it.--topicksparquet(default),csv,tsvorjsonl. Each table is read page by page, so its size does not matter. A file that is already there is skipped unless--overwrite. One failed table does not stop the rest; the exit code is 1 if any failed. On a three-level engine name the catalog with--db-catalog. --db-export-tables T1,T2- Tables for
--db-export, comma separated. Default: every table of the schema. --db CONNECTION- Saved connection name (case-insensitive) or id for the
--db-*actions. Connections are managed in Settings -> Databases; passwords stay in the system keyring. --mcp- Start a Model Context Protocol (MCP) server on standard
input / output. The tools exposed are:
read_table,tail,sample,schema,list_tables,count_rows,run_sql,convert,export_schema,profile,find_duplicates,fuzzy_duplicates,value_frequency,search,compare_schemas,diff_tables,describe_file,validate_against_schema,unique_columns,pivot,correlation,grep_files,write_table,edit_table,transform_columns,anonymize,detect_pii,detect_outliers,fill_missing,drop_duplicates,union_tables,join_tables,partition_table,list_db_connections,list_db_tables,query_db,write_db_table,copy_db_table. The file-writing tools (convert,write_table,edit_table,transform_columns,anonymize,partition_table,write_db_table,copy_db_table) are dropped when--mcp-read-onlyis given. Defaults for the row limit and per-cell byte cap come from the user's Octa settings (Settings → MCP). See the MCP server guide for setup. --mcp-read-only- Only valid with
--mcp. Omits the file-writing tools (write_table,edit_table,convert) so the server exposes a read-only surface. --mcp-tools LIST- Only valid with
--mcp. Advertise ONLY these tools, comma-separated. Takes group names (core,quality,compare,combine,reshape,databases,cloud,write) and individual tool names, mixed freely:--mcp-tools core,databasesor--mcp-tools read_table,run_sql. Without it every tool is advertised. A tool left out is also not callable, and an unrecognised name stops the server rather than starting one with the wrong surface. This is the lever for an agent's context size: the client reads the tool list once and carries it in every request to its model, and all of Octa's tools together are roughly 39,000 tokens. See Advertising fewer tools. --mcp-without LIST- Only valid with
--mcp. Advertise everything except these, in the same spelling as--mcp-tools. Applied after it, so the two combine:--mcp-tools core,write --mcp-without convert.
--batch-convert, --harmonise-schema, --partition-by, --db-copy and
--db-export report progress on stderr while they run, on a single line that rewrites
itself ([137/500] file.csv ETA 1:12). The line appears only when
stderr is a terminal, so redirected output and CI logs are unchanged.
--completions SHELL- Print a shell completion script for SHELL to standard output and
exit. One of
bash,zsh,fish,powershell,elvish. The script is generated from Octa's own argument list, so it completes every flag this page documents. Wire it into the running shell witheval "$(octa --completions zsh)", or letinstall.shwrite the files under the install prefix. See--completions. --cloud-ls URL- List a cloud bucket or prefix (
s3://,az://,gs://,sftp://,davs://). One folder level by default; add--recursiveto flatten everything under the prefix. Credentials come from a saved connection covering the URL, else the ambient chain. See Cloud CLI. --cloud-get URL- Download one cloud object to the file given by
--out. --cloud-put FILE- Upload a local file to the cloud URL given by
--to. --cloud-copy URL- Copy a cloud object, or a whole prefix (source ending in
/), to the URL given by--to. Within one bucket the backend copies server-side; across buckets, accounts or providers the object is streamed in blocks, so object size does not drive memory. At most 10,000 objects per run. --cloud-move URL- Like
--cloud-copy, then delete the source. Object stores have no rename. The delete runs only after every copy succeeded. --cloud-delete URL- Delete a cloud object, or a whole prefix with
--recursive. Cannot be undone unless the bucket has versioning enabled. --list-connections- Print the saved cloud and database connections (names and targets only, never secrets).
--add-connection SPEC- Add or update a saved connection from a
key=value,key=valuespec.kind=andname=are required. A connection with the same name is replaced wholesale, keeping its id and stored secret, so re-running a provisioning script is idempotent; omitted keys revert to defaults. Unknown keys are an error. Cloud (kind=s3|azure|gcs): bucket, region, endpoint, prefix, account, profile, account_level, anonymous, allow_writes, force_path_style, allow_http. Database (kind=postgres|mysql|mssql|oracle|redshift|clickhouse|exasol|trino|athena|snowflake|databricks|bigquery): host, port, database, user, allow_writes, query_timeout. Only password authentication can be expressed here; other methods need the Settings dialog.query_timeoutis whole seconds (default 60) and is read only by the engines that poll over HTTP for their results (Trino, Athena, Snowflake, Databricks, BigQuery). --remove-connection NAME- Remove a saved connection by name or id, and delete its stored secret.
--secret-env VAR- Name of an environment variable holding the secret for
--add-connection, so it never appears in argv. Database: the password. S3: ACCESS_KEY_ID:SECRET_ACCESS_KEY[:TOKEN]. Azure: the account key or a SAS token. GCS uses application-default credentials and takes no secret.
Options¶
-n N,--lines N- Row count for
--head,--tail, and--sample. Default 20. --sort-by COLS- Sort the rows of
--heador--convertby these columns, first key first (comma-separated or repeated). Sorts the rows that were read, up to the row cap: add--rows allto sort a whole large file. With--headthe sort happens before the first N rows are taken. --sort-desc COLS- The
--sort-bycolumns that sort high to low. --natural- Sort text naturally (file2 before file10) instead of letter by letter, the default.
--seed N- Seed for
--sampleand--test-data, for reproducible output. Default 0. -q QUERY,--query QUERY- SQL query string for
--sql. Always reference the file's data as the table data. --sql-table NAME=PATH- For
--sqlonly. Register an extra file as a workspace table named NAME. Any supported format. Repeatable. --sql-attach ALIAS=PATH- For
--sqlonly.ATTACHa DuckDB or SQLite database under ALIAS. Repeatable. After attachment every inner table is queryable asalias.schema.tbl(DuckDB) oralias.tbl(SQLite when the DuckDB sqlite extension is bundled, otherwise fallback registration underalias__table). --sql-write-to PATH- For
--sqlonly. Persist the SELECT result to PATH instead of printing it. PATH's extension picks DuckDB (.duckdb,.ddb) or SQLite (everything else). The file is created if missing. Requires--sql-write-table;--sql-write-schemaand--sql-write-modeare optional. --sql-write-table TABLE- Target table name for
--sql-write-to. --sql-write-schema SCHEMA- Target schema for
--sql-write-to. DuckDB only; defaults tomain. SQLite has no schemas; passmainor leave unset. --sql-write-mode MODEcreate(default; errors if the table already exists),replace(drop + recreate), orappend(INSERTinto the existing table). Column count and order must match in append mode.-t TARGET,--target TARGET- Output target for
--export-schema. TARGET is one ofpostgres(default),mysql,sqlite,databricks,snowflake,pydantic,typescript,json-schema, orrust. --table-a NAME,--table-b NAME- For
--compare-schemasonly: pick a specific table on each side when the source is multi-table (SQLite, DuckDB, GeoPackage). --table NAME- For
--describe,--validate-schema, and--unique-columns: pick a specific table on the file when the source is multi-table. --expect-schema SCHEMA_FILE- Path to the expected JSON Schema for
--validate-schema. Required by that action. --sample-rows N- Number of preview rows for
--describe(default 5, clamped to 100). --deep- With
--describe, also report how the file is physically written: row groups, compression codec, encodings and per-column statistics, followed by one row per column per row group. Layout hints go to standard error, so a piped run stays parseable. Parquet reports full detail; other formats report their size and state that they expose no inspectable structure. Place it after the file:--describe FILE --deep. See File Internals. --compression CODEC- Compression codec for the written file:
uncompressed,snappy,zstd,gziporlz4. Applies to--convertand--batch-convert; Parquet targets only, ignored by other formats. An unknown name is rejected before any file is written. Omitted, the codec saved in Settings > Files > Write options is used, orzstdwhen nothing is saved. --row-group-size N- Rows per Parquet row group for
--convertand--batch-convert. Larger groups scan faster; smaller groups let readers skip more precisely. Omitted means the writer's own default. --max-combo N- Max combo size for
--unique-columns(default 1, clamped to[1, 3]). --rows N|all- Override the initial-load row cap for this invocation. Streaming
formats (Parquet, CSV, TSV) honour a process-wide cap (default
2,000,000 rows);
--rows 10,000,000raises it,--rows alldisables it entirely. Applies to--schema,--head,--convert, and--sql. Commas / underscores in the number are allowed for readability. --dedupe-on COLS- Comma-separated key columns for
--dedupe. Absent = whole-row key. --dedupe-keep WHICH- Which duplicate to keep for
--dedupe:first(default) orlast. --outlier-method M- Method for
--outliers:iqr(default) orzscore. --outlier-cols COLS- Comma-separated columns to scan with
--outliers(default: all). --outlier-k K- Threshold multiplier for
--outliers(default 1.5 IQR, 3.0 z-score). --pii-sample N- Rows sampled per column for
--detect-pii(default 500). --union-file FILE- Additional source for
--union(repeatable); positional file plus these form the input list (minimum two). --union-drop COL- Column to omit from the
--unionoutput (repeatable). --union-cast COL=TYPE- Override a column's target Arrow type in the
--unionoutput (repeatable). --join-file FILE- Additional source for
--join(repeatable). --join-on COLS- Key column(s) for
--join; comma-separated or repeated. Required. --join-type TYPE- Join strategy for
--join:left(default),inner,right,full,semi(rows of the first file with a partner),anti(rows without one) orasof(the last--join-onkey matches the nearest earlier value). --out-dir DIR- Output directory for
--partition-by,--split-rows,--split-size,--batch-convert,--harmonise-schemaand--db-export(required; created if absent). --partition-format EXT- Output extension for
--partition-by(default: the source's extension). --split-rows N- At most N rows per output file. Works alone (splits the whole
table) or with
--partition-by(splits each group). Every file then gets a part number:name_part_0001.ext, orpart-0001.extinside a folder. --split-size SIZE- At most SIZE per output file, e.g.
25MB,500KB,1GB(1 MB = 1,000,000 bytes;MiBfor 1,048,576). Works alone or with--partition-byand--split-rows. Files land a little under the limit. -f FORMAT,--format FORMAT-
Output format for actions that print a table. FORMAT is one of:
tsv(default): tab-separated values, one row per line, header row first. TAB and newline characters in cells are replaced with spaces (TSV has no escape mechanism).json: pretty-printed JSON array of{column: value}objects. Numeric and boolean cells keep their native JSON types; dates, blobs, and nested values become strings.csv: RFC 4180 CSV. Fields with comma, quote, or newline are properly quoted; embedded quotes are doubled.
--formathas no effect for--convert(output format is taken from the output path's extension),--export-schema(which emits source code chosen by-t), or--mcp. -h,--help- Print the flag list and exit.
-hand--helpproduce the same output. --help-all- Print the flag list plus worked examples for every action, and exit. The examples are behind their own flag because printing them by default buried the flag list under something the size of this manual page.
--version- Print the Octa version and exit.
Output Streams¶
Tabular data is written to stdout. Status messages, warnings,
and errors are written to stderr. This means
octa --sql FILE -q QUERY -f json | jq ... is safe even when an
error occurs, since the data stream stays clean.
Exit code is 0 on success and 1 on any error (invalid
arguments, file-not-found, parse failure, write rejection, etc.).
--validate-schema also exits 1 on a successful read where
the schemas differ, and --schema-drift exits 1 on a successful
scan where the files disagree, so CI pipelines can gate on the schema
directly. --drift-report exits 1 when a --fail-on gate was breached,
--merge while a conflict is open, --recipe when a step could not run, --check exits 1 on any
failing or unrunnable rule, and --compare-folders when any file differs
or is missing.
Examples¶
Open multiple files in the GUI:
Print the schema of a Parquet file:
Print the first 5 rows of a CSV as JSON:
Print the last rows / a reproducible random sample:
Convert formats:
Group-by aggregation:
Read every row of a huge file:
octa --sql huge.parquet -q 'SELECT count(*) FROM data' --rows all
octa --head huge.parquet -n 100 --rows 10,000,000
Pipe a SQL result through jq:
JOIN across formats:
octa --sql sales.parquet \
--sql-table customers=customers.csv \
-q 'SELECT c.name, SUM(d.amount) FROM data d
JOIN customers c ON d.cid = c.cid GROUP BY c.name'
ATTACH a DuckDB warehouse and JOIN against it:
octa --sql sales.parquet \
--sql-attach wh=warehouse.duckdb \
-q 'SELECT count(*) FROM data d
JOIN wh.main.products p ON d.cid = p.cid'
Write a SQL result back to a DuckDB schema:
octa --sql sales.parquet -q '
SELECT region, SUM(amount) AS total FROM data GROUP BY region
' --sql-write-to analytics.duckdb \
--sql-write-schema reports \
--sql-write-table q4_summary
Export a schema as Snowflake DDL or a Pydantic model:
Diff the schemas of two files:
octa --compare-schemas v1.parquet v2.parquet
octa --compare-schemas a.sqlite b.sqlite --table-a users --table-b users -f json
Diff two files' rows:
octa --diff v1.csv v2.csv
octa --diff v1.csv v2.csv --diff-mode ordered
octa --diff v1.csv v2.csv --diff-mode join --diff-on id
octa --diff a.parquet b.parquet -f json
One-shot file snapshot:
Validate a file against a JSON Schema in CI:
octa --export-schema sales.parquet -t json-schema > sales.schema.json
octa --validate-schema sales.parquet --expect-schema sales.schema.json
Find primary-key candidates:
Browse a live database connection:
octa --db-tables --db warehouse
octa --db-tables --db warehouse --db-catalog sales_prod
octa --db-query "SELECT * FROM public.users LIMIT 10" --db prod
Copy a table between two servers, replacing the target:
Start the MCP server:
Environment¶
OCTA_CONFIG_DIR- Directory holding
settings.toml, used verbatim on every platform and taking precedence overXDG_CONFIG_HOME/HOME/APPDATA. Required in containers, which usually set none of those; without it Octa reports that it has no config directory rather than silently running without settings. OCTA_DEBUG- Set to 1 to force debug mode on for one run without changing the saved setting: verbose logging, plus one log line per mouse press and release recording its position, whether it counted as a click, and which layer of the interface it reached. Intended for diagnosing the GUI itself, when the Settings dialog is the thing misbehaving and its checkbox cannot be reached. A build from source additionally outlines every clickable area on screen; release binaries cannot, since the toolkit compiles that drawing out. See Diagnostics.
OCTA_NO_KEYRING- Set to 1 to skip the OS keyring entirely, so secrets are read from
and written to
settings.toml(chmod 0600). Secrets already fall back to the file when the keyring errors; this avoids the lookup on systems where it can never work, such as a container with no D-Bus. AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AZURE_STORAGE_ACCOUNT,GOOGLE_APPLICATION_CREDENTIALS- Ambient cloud credentials, used when no saved connection covers a URL.
Files¶
$XDG_CONFIG_HOME/octa/settings.toml- Linux. User settings. Created on first launch with defaults. See Settings reference for every key.
$HOME/Library/Application Support/Octa/settings.toml- macOS. Same purpose.
%APPDATA%\Octa\settings.toml- Windows. Same purpose.
MCP Server¶
When invoked with --mcp, Octa speaks the Model Context Protocol
over JSON-RPC on stdin/stdout. The tools are:
read_table(path, limit?, unlimited?, table?)returns schema + rows JSON.tail(path, n?, unlimited?, table?)returns the last N rows.sample(path, n?, seed?, unlimited?, table?)returns a reproducible random N-row sample.schema(path, table?)returns column schema only.list_tables(path)lists tables for multi-table sources (SQLite / DuckDB / GeoPackage).count_rows(path, unlimited?, table?)returns the row count for a tabular file.run_sql(path, query, limit?, unlimited?, table?)runs DuckDB against the file as tabledata.convert(input, output, unlimited?, table?)exposes the same surface as--convert.export_schema(path, target, table?)renders the schema as DDL / a model / a struct.profile(path, unlimited?, table?)returns per-column statistics viaSUMMARIZE.find_duplicates(path, key_columns, …, unlimited?)returns rows sharing key-column values.fuzzy_duplicates(path, key_columns, method?, threshold?, …)clusters near-duplicate rows (typos, spacing, word order).value_frequency(path, column, …, unlimited?)counts per-column values.search(path, query, mode?, …, unlimited?)matches cells across every column.compare_schemas(path_a, path_b, table_a?, table_b?)diffs the column schemas of two files.diff_tables(path_a, path_b, mode?, on?, …)row-level diff of two files (set / ordered / join).describe_file(path, table?, sample_rows?, unlimited?)returns a one-shot orientation snapshot.validate_against_schema(path, table?, schema_path?, schema_inline?)checks a file against a JSON Schema.unique_columns(path, table?, max_combo_size?, unlimited?)finds primary-key candidates.pivot(path, mode?, on?, value?, agg?, …)reshapes long <-> wide (PIVOT / UNPIVOT).correlation(path, method?, unlimited?, table?)computes a pairwise numeric correlation matrix.grep_files(dir, query, mode?, …)greps a value across files in a directory.write_table(path, columns, rows?, mode?, unlimited?)writes inline rows to a new file (create / overwrite / append).edit_table(path, table?, set?, insert_rows?, delete_rows?, unlimited?)edits an existing file in place (DB sources diff-saved).transform_columns(path, drop?, rename?, cast?, output_path?)renames / casts / drops columns and writes back.anonymize(path, rules, salt?, output_path?)masks / scrambles columns and writes the result.detect_pii(path, sample_rows?, table?)reports likely personal-data columns.detect_outliers(path, columns?, method?, k?, table?)flags numeric outlier cells.fill_missing(path, column, strategy, value?, table?)imputes empty cells in a column.drop_duplicates(path, on?, keep?, table?)removes duplicate rows.union_tables(sources, drop?, cast?)stacks tables vertically.join_tables(sources, on, how?)joins tables on key columns.partition_table(path, out_dir, column?, max_rows?, max_bytes?, format?, table?)writes one file per distinct column value and/or parts of bounded size.
The file-writing tools (convert, write_table, edit_table,
transform_columns, anonymize, partition_table) are dropped under
--mcp-read-only.
Defaults (the response row cap of 1000 rows, per-cell byte cap of
64 KiB, and file-loader cap of 2,000,000 rows) are configurable
under Settings → MCP and
Settings → Performance. They are read once at server startup;
changes require a restart. Per-call, pass limit: 0 to lift the
response cap and unlimited: true to lift the file-loader cap so
the tool sees every row on disk. Parquet files with very many row
groups fall back to a DuckDB-backed reader automatically. See
Limits & truncation for the full
mechanics.
See Also¶
man(1), jq(1), duckdb(1), parquet-tools(1)
- Project homepage: https://github.com/thorstenfoltz/octa
- Online documentation: https://thorstenfoltz.github.io/octa/
- Common tasks covers worked CLI workflows (CSV → Parquet pipelines, JSON-line filtering, etc.).
Bugs / Feedback¶
Report bugs at https://github.com/thorstenfoltz/octa/issues.
Author¶
Thorsten Foltz
Copyright¶
Copyright © 2026 Thorsten Foltz. Licensed under the MIT license.