Skip to content

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] [-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 [--rows N|all]
octa --sql FILE -q QUERY [-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]
octa --diff FILE --diff-db CONN --diff-db-table TABLE [--diff-mode MODE]
     [--diff-on COLS] [-f FORMAT]
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 --check FILE --rules RULES.toml [-f FORMAT]

octa --relationships DIR [--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 --db-tables --db CONNECTION [--db-catalog NAME] [-f FORMAT]
octa --db-query SQL --db CONNECTION [-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 --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, --check, --relationships, --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). 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 (5,000,000 rows by default) and projects the schema from that. See octa --schema for 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. See octa --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 --rows to 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 (5 M rows by default); pass --rows all to convert the full file. See octa --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=PATH and 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) on data itself do not persist back to FILE, because the in-memory DuckDB connection is discarded at exit. See octa --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 (default postgres); only the column list is read. See octa --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-b to pick a specific table on multi-table sources. See octa --compare-schemas.
--diff FILE_A FILE_B
Compare two files. --diff-mode selects the strategy; the output is a table tagged by a leading status column, 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 (default set): set compares rows by whole-row content (every column, positionally) and prints rows unique to each side (only_in_a / only_in_b). ordered lines up row i of A with row i of B and prints matched rows that differ as changed (with a changed_columns column) plus any trailing only_in_a / only_in_b rows. join matches rows on the --diff-on key 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 is join; ignored otherwise.
--diff-db CONN
Compare against a live database table instead of a second file. CONN names a saved database connection. With this set, --diff takes a single positional file: that file is side A and the table is side B, so only_in_a means "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-db connection, as SCHEMA.TABLE or CATALOG.SCHEMA.TABLE (the three-part form is for 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 N to change the preview size (default 5, max 100), and --deep to add the file's physical layout. See octa --describe.
--validate-schema FILE
Check FILE's column schema against the JSON Schema given by --expect-schema SCHEMA_FILE. Exit code is 0 on a clean match and 1 otherwise, which is CI-pipeable. Schemas produced by --export-schema -t json-schema round-trip cleanly. See octa --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 0 when every file agrees and 1 otherwise, which is CI-pipeable. Add --recursive to walk subdirectories (depth 8) and --ignore-case to treat names differing only in case as one column. See octa --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 0 unless --fail-on was given and a gate was breached. See octa --drift-report.
--fail-on SPEC
Turn --drift-report into a CI gate. SPEC is a comma-separated list of metric:change pairs, for example null_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 from 0 to 0.5 breaches any gate. Metric names are rows, null_rate, distinct_count, min, max and mean.
--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 kinds not_null, unique, range (with min and max), regex (with pattern) or max_length. A rule without a column applies 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 is 1 on 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. See octa --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 exits 0: this is a report, not a gate. Add --recursive to walk subdirectories. See octa --relationships.
--stream
Let DuckDB scan the file where it lies instead of loading its rows. Applies to --sql over 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 named data rather than as a loaded table, so --rows no 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-file when 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 is 0 when nothing was refused and 1 otherwise. Add --recursive, --ignore-case, and --overwrite. See octa --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 LIST picks a comma-separated subset of stats, distributions, top_values and correlation (default: all four); an unknown name is an error. --report-sample N profiles a random sample of N rows and says so in the output. See octa --report.
--fuzzy-join FILE
Join FILE to each --fuzzy-join-file on how similar the values are rather than on exact equality, for tables that name the same thing differently. --fuzzy-on LEFT=RIGHT names the columns to compare and may be repeated (scores are averaged). --fuzzy-method is edit_ratio (default), jaro_winkler or token_set; --fuzzy-threshold is the minimum average score (default 0.85); --fuzzy-block LEFT=RIGHT compares only rows agreeing exactly on those columns; --fuzzy-join-type is inner, left (default), right or full; --fuzzy-max-rows caps the rows per side (default 20000). Each left row keeps its single best partner and the output gains match_score_N and ambiguous_N per step. See octa --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. See octa --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 shared salt; 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. See octa --anonymize.
--dedupe FILE
Remove duplicate rows from FILE and print the result. Without --dedupe-on the whole row is the key; --dedupe-keep (first/last) picks the surviving occurrence. See octa --dedupe.
--impute COL=STRATEGY
Fill missing cells of a column of the positional FILE (repeatable). Strategies: mean, median, mode, ffill, bfill, const:VALUE. See octa --impute.
--outliers FILE
Flag numeric outlier cells in FILE. --outlier-method (iqr/zscore), --outlier-cols, --outlier-k tune the scan. See octa --outliers.
--detect-pii FILE
Scan FILE for likely PII columns (email, phone, IBAN, credit card, SSN). --pii-sample N sets the per-column sample size. See octa --detect-pii.
--union FILE
Stack the positional FILE plus every --union-file into one table. --union-ignore-case merges column names differing only in case. --union-drop omits columns, --union-cast COL=TYPE overrides a target type. See octa --union.
--join FILE
Join the positional FILE(s) plus every --join-file on the --join-on key(s). --join-type is left/inner/right/full. See octa --join.
--partition-by COL
Split the positional FILE into one file per distinct value of COL, written into --out-dir. --partition-format sets the output extension. See octa --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, _3 suffixes in input order, so a run cannot overwrite its own earlier output. Existing outputs are skipped unless --overwrite is given. One failed file does not stop the run; stdout carries a headerless input<TAB>output<TAB>status listing, errors and the summary go to stderr, and the exit code is 1 when any file failed. See octa --batch-convert.
--resample COL
Group the positional FILE into time buckets: one row per --interval of COL. --interval is minute, hour, day (default), week, month, quarter or year; --value-cols (required) names the columns to aggregate; --agg is sum (default), mean, min, max, count, first or last; --group-by adds extra grouping columns, giving one series per combination. The time column is cast with TRY_CAST, so a row that will not parse buckets as NULL instead of failing the run. See octa --resample.
--rolling COL
Add a rolling aggregate of COL over the previous --window N rows (including the current row). --order-by COL is required - a rolling aggregate over unordered rows is meaningless. --agg is mean (default), sum, min, max, count, first or last; --partition-by-cols restarts the frame per group (spelled that way because --partition-by is the split-into-files action). See octa --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 Snowflake, Databricks and BigQuery, which have a catalog level above the schema, the catalogs themselves are listed unless --db-catalog names one.
--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 --rows to raise or lift it.
--db-write-table SCHEMA.TABLE
Write the positional FILE into the given server table on the --db connection. --db-write-mode is create (default; error if the table exists), append, or replace. Refused unless the connection allows writes.
--db-catalog NAME
Catalog (the top namespace level) for a three-level engine, used by --db-tables and --db-write-table. Only Snowflake, Databricks and BigQuery have one; passing it to any other engine is an error. With --db-tables and no catalog, the catalogs themselves are listed.
--db-copy SCHEMA.TABLE
Copy a table from the --db connection to another saved connection, server to server. Requires --db-copy-to. --db-write-mode selects create (default), append or replace. 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-copy on a three-level engine.
--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-only is 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.
--cloud-ls URL
List a cloud bucket or prefix (s3://, az://, gs://). One folder level by default; add --recursive to 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=value spec. kind= and name= 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|redshift|clickhouse|exasol|snowflake|databricks|bigquery): host, port, database, user, allow_writes. Only password authentication can be expressed here; other methods need the Settings dialog.
--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.
--seed N
Seed for --sample, 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 --sql only. Register an extra file as a workspace table named NAME. Any supported format. Repeatable.
--sql-attach ALIAS=PATH
For --sql only. ATTACH a DuckDB or SQLite database under ALIAS. Repeatable. After attachment every inner table is queryable as alias.schema.tbl (DuckDB) or alias.tbl (SQLite when the DuckDB sqlite extension is bundled, otherwise fallback registration under alias__table).
--sql-write-to PATH
For --sql only. 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-schema and --sql-write-mode are 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 to main. SQLite has no schemas; pass main or leave unset.
--sql-write-mode MODE
create (default; errors if the table already exists), replace (drop + recreate), or append (INSERT into the existing table). Column count and order must match in append mode.
-t TARGET, --target TARGET
Output target for --export-schema. TARGET is one of postgres (default), mysql, sqlite, databricks, snowflake, pydantic, typescript, json-schema, or rust.
--table-a NAME, --table-b NAME
For --compare-schemas only: 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, gzip or lz4. Applies to --convert and --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, or zstd when nothing is saved.
--row-group-size N
Rows per Parquet row group for --convert and --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 5,000,000 rows); --rows 10,000,000 raises it, --rows all disables 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) or last.
--outlier-method M
Method for --outliers: iqr (default) or zscore.
--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 --union output (repeatable).
--union-cast COL=TYPE
Override a column's target Arrow type in the --union output (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.
--out-dir DIR
Output directory for --partition-by (required; created if absent).
--partition-format EXT
Output extension for --partition-by (default: the source's extension).
-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.

--format has 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. -h and --help produce 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, and --check exits 1 on any failing or unrunnable rule.

Examples

Open multiple files in the GUI:

octa file1.csv file2.parquet file3.json

Print the schema of a Parquet file:

octa --schema sales.parquet

Print the first 5 rows of a CSV as JSON:

octa --head data.csv -n 5 -f json

Print the last rows / a reproducible random sample:

octa --tail data.csv -n 5
octa --sample data.parquet -n 20 --seed 1

Convert formats:

octa --convert in.csv out.parquet
octa --convert workbook.xlsx tidy.sqlite

Group-by aggregation:

octa --sql sales.parquet -q 'SELECT region, SUM(amount) FROM data GROUP BY region'

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:

octa --sql users.parquet -q 'SELECT email FROM data WHERE active' -f json \
  | jq -r '.[].email'

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:

octa --export-schema sales.parquet -t snowflake
octa -e users.parquet -t pydantic > users_model.py

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:

octa --describe data.parquet --sample-rows 3
octa --describe users.sqlite --table customers -f json

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:

octa --unique-columns users.csv
octa --unique-columns orders.parquet --max-combo 2 -f json

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:

octa --db prod --db-copy analytics.orders \
     --db-copy-to warehouse --db-write-mode replace

Start the MCP server:

octa --mcp

Environment

OCTA_CONFIG_DIR
Directory holding settings.toml, used verbatim on every platform and taking precedence over XDG_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:

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 5,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)

Bugs / Feedback

Report bugs at https://github.com/thorstenfoltz/octa/issues.

Author

Thorsten Foltz

Copyright © 2026 Thorsten Foltz. Licensed under the MIT license.