Octa
An application for viewing data files. Octa opens files in a spreadsheet-like table view with sorting, filtering, and search options, locally or straight from cloud object storage (S3, Azure, GCS). Including CLI, MCP and a chat assistant within the GUI.
📚 Documentation: https://thorstenfoltz.github.io/octa/
Contents
Preview

Why Octa?
One native tool to open, inspect, query, and compare data files across 25+ formats (Parquet, CSV, JSON, Excel, SQLite, DuckDB, GeoPackage, Arrow, Avro, ORC, SAS, SPSS, Stata, RDS, HDF5, NetCDF, DBF, NumPy, MessagePack, BSON, GeoJSON, Shapefile, Delta Lake, Apache Iceberg, EPUB, archives, and more) without spinning up Python, opening a browser, or installing a heavyweight database client. Octa runs as a standalone binary on Linux, macOS, and Windows.
Files do not have to be local: Octa browses and opens objects directly from Amazon S3 (and S3-compatible providers), Azure Blob Storage, and Google Cloud Storage, with saving back to the cloud available as an opt-in.
The same binary also speaks the Model Context Protocol over stdio (octa --mcp), so AI assistants and automation pipelines can read local files directly through Octa instead of round-tripping data through a custom script.
Supported Formats
| Format | Read | Write | Notes |
|---|---|---|---|
| Parquet | yes | yes | Lazy row loading for very large files |
| CSV/TSV | yes | yes | Auto-detected delimiter |
| JSON/JSON Lines | yes | yes | Collapsible JSON Tree view with inline key / value editing. |
| Excel | yes | yes | Opens every sheet as a tab. |
| ODS | yes | yes | |
| Arrow IPC / Feather | yes | yes | |
| Avro | yes | yes | |
| ORC | yes | yes | |
| HDF5 | yes | no | |
| NetCDF v3 (.nc) | yes | no | |
| SQLite | yes | yes | Multi-table picker; diff-based writes via rowid identity. |
| DuckDB | yes | yes | Multi-table picker; SQL Query view exposes the file as data. |
| GeoPackage (.gpkg) | yes | yes | Multi-table picker. |
| SAS (.sas7bdat) | yes | no | |
| SPSS (.sav, .zsav) | yes | yes | |
| Stata (.dta) | yes | yes | |
| R (.rds, .rdata) | yes | no | |
| NumPy (.npy, .npz) | yes | no | .npz opens each array as its own tab. |
| MessagePack (.msgpack) | yes | no | Decoded like JSON. |
| BSON (.bson) | yes | no | Concatenated documents (mongodump shape); decoded like JSON. |
| DBF/dBase (.dbf) | yes | yes | |
| XML | yes | yes | |
| TOML | yes | yes | |
| YAML | yes | yes | Collapsible YAML Tree view (mirrors JSON Tree). |
| Jupyter Notebook | yes | yes | Notebook view renders code + markdown cells with syntect highlighting. |
| Markdown | yes | yes | Rendered preview, Split, and Edit modes. |
| EPUB | yes | no | EPUB Reader view, chapter-by-chapter with embedded images. |
| GeoJSON (.geojson) | yes | no | Map view with OSM tile rendering or geometry-only fallback. |
| Shapefile (.shp) | yes | no | Reads sibling .dbf attributes; Map view like GeoJSON. |
| Delta Lake (folder) | yes | no | Open via File -> Open table folder...; extensions download on first use. |
| Apache Iceberg (folder) | yes | no | Open via File -> Open table folder...; extensions download on first use. |
| Archive (zip / tar / tgz) | yes | no | Read-only listing; per-entry extract-and-open action. |
| Fixed-width (FWF) | yes | no | .fwf / .prn; read-only, best-effort column-boundary inference. |
| Source code / config | yes | yes | .py, .rs, .go, .ts, ... opened with syntect highlighting in the Raw view. |
| Plain Text | yes | yes |
Unknown file extensions are opened as plain text.
Features
Most behaviour is configurable, and the documentation covers every option, default, and keyboard shortcut in detail.
Table View
- Virtual table rendering with smooth scrolling for large datasets
- Lazy row loading for Parquet files, handling millions of rows
- Inline cell editing with type-aware parsing
- Column resize, drag-and-drop reorder, and double-click best-fit width
- Ascending/descending sort by any column
- Cell, row, and column selection with clipboard copy/paste
- Search and filter across all columns in real time (Plain / Wildcard / Regex modes), with match-case and whole-word toggles, a single-column scope, and a persistent Recent history
- Excel-style formulas in cells (
=A1+B1) and as the "Insert column" formula - Thousand separators for numeric cells (English / European styles) plus per-column rounding, all display-only and never written to saved data
- Clickable web links: cells holding an
http/httpsaddress show as underlined links and Ctrl+click opens them in your browser (on by default, toggle in Settings)
Cloud Storage
- Browse and open files directly from Amazon S3 (and S3-compatible providers such as IONOS, MinIO, and Cloudflare R2), Azure Blob Storage, and Google Cloud Storage
- A File → Cloud connections sidebar lists saved buckets; expand folders and click a file to open it in a tab, exactly like a local file, so every supported format works
- Credentials your way: a static key / SAS token saved on the connection, browser SSO through the cloud's own CLI (
aws sso login,az login,gcloud auth ... login), ambient environment credentials, or no credentials at all for public/anonymous buckets. Saved secrets go into the OS keyring when available - Saving back to the cloud is off by default (
Settings → Cloud storage → Allow writing to cloud storage); cloud-opened files are read-only until you opt in - The CLI assistant and MCP server understand
s3://,az://, andgs://URLs and can read (and, when allowed, write) cloud objects like local files; alist_objectsMCP tool browses a bucket
Multiple View Modes
- Table — structured spreadsheet display (default)
- Raw Text — source text with line numbers and optional column alignment. Syntect-based syntax highlighting kicks in for languages with no dedicated view (Python, Rust, shell, Terraform, etc.)
- Markdown — rendered CommonMark preview with Preview / Split / Edit toggle; Split places a TextEdit next to a live preview.
- JSON Tree / YAML Tree — collapsible Firefox-style tree for
.json/.jsonl/.yaml/.yml. Keys are renamable, values are editable, and you can add keys to objects in place. - Notebook — rendered Jupyter notebook with code cells, markdown cells, and outputs.
- EPUB Reader — chapter-by-chapter rendered text for
.epubfiles. Top toolbar shows the book title, Previous/Next, and a chapter combo. Embedded images render as a thumbnail strip below the chapter body. - Map — slippy-map view for
.geojsonand.shpfiles, and for any table with latitude / longitude columns (auto-detected, with a Lat / Lon picker). OSM tiles (configurable URL) with feature geometries painted on top. Toolbar toggles Tiles ↔ Geometry-only; plain mouse-wheel zoom; double-click to zoom in. - Compare — side-by-side comparison of two files. Four sub-modes toggle in the Compare toolbar: Text Diff (git-style line diff), Row Hash Diff (BLAKE3-hashed columns; uniques + shared rows bucketed), Ordered (rows lined up positionally with the exact changed cells), and Join (rows matched on a key column into added / removed / changed). Cross-format works since hashing sees cell text only. For a file under git, you can compare the current version against a recent commit, or open an older revision in its own tab.
- SQL Query — write a query against the current table (exposed as
data) and see results beneath. Line numbers, chip-style autocomplete, UPPER/lower case conversion, a per-tab query History, a saved-Snippets library, and a brief green highlight of the cells a mutation changed.
Editing
- Insert, delete, and move rows and columns
- Colour marking for cells, rows, and columns with six colour choices
- Conditional formatting: rule-based automatic cell colouring (equals, contains, greater-than, is empty, ...) that applies live
- Data validation: flag cells that break a rule (not empty, in range, matches a pattern, unique, max length) — failing cells are highlighted red
- Multi-column sort (Analyse → Sort by columns...): sort by an ordered list of columns, each ascending or descending
- Transform column (Edit → Transform column...): split, merge, fill down/up, extract a regex match, or find-and-replace within a column; new-column ops let you set the name and position
- Copy the current selection as a GitHub-flavoured Markdown table
- Undo / Redo for cell edits, structural changes, and colour marks
- Leading/trailing whitespace trimmed from string cells and column titles on load (configurable, with a banner listing the affected columns)
- Tidy up (Data → Tidy up...): trim stray spaces and optionally convert column names to
snake_casefor the current table, on demand and in one undoable step - Unsaved-changes guards on close and file open
- Save in the original format or export to a different one via Save As
- Optional auto-save writes changed files back to disk on a timer (off by default; enable it and set the interval in minutes under Settings → Files)
- Reopen Last Closed Tab (default Ctrl+Shift+T) restores accidentally-closed tabs
- Find duplicates (default Ctrl+Shift+D) picks dedupe-key columns and either highlights duplicate rows or opens them in a new tab
Inspecting data
- Summary — one row of statistics per column (min, max, sum, mean, median, standard deviation, range, IQR, quartiles, mode and its count, null counts,
exact unique count, distinct ratio, text length, total rows). Headers are short
snake_caseidentifiers so the table is easy to reuse, with the localised description on hover; you choose which statistics appear under Settings → Summary - Value Frequency —
value_counts()-style top-N values for any column. Numeric columns can be turned into a histogram: type a bin count (or leave it for automatic Sturges binning) and get that many equal-width ranges with their counts - Pivot / Unpivot — reshape a table between long and wide form (DuckDB
PIVOT/UNPIVOT) into a new tab - Transpose — swap the table's rows and columns into a new tab (Analyse → Transpose)
- Random sample — open a new tab of a chosen number of randomly picked rows (Analyse → Random sample...)
- Schema Export — render the column list as Postgres / MySQL / SQLite / Databricks / Snowflake DDL, Pydantic v2, TypeScript interface, JSON Schema, or a Rust struct. Also available from the CLI (
octa --export-schema) and over MCP. - Chart — open a new tab plotting the active table as a histogram, bar, line, scatter, or box chart via
egui_plot. Customisable title / axis / legend / per-series colour, PNG/SVG/PDF export, log scale.
Archives
.zip, .tar, and .tgz files open as a read-only table listing
each entry's path, size_bytes, compressed_bytes, mtime,
is_dir, and type. An action bar above the table extracts the
selected entry into a tempfile and opens it as a new tab through
the normal file-open path — so any reader Octa supports (CSV,
JSON, Parquet, …) works on entries inside an archive. .tar.gz is
not auto-routed; rename to .tgz or use File → Open → All
files.
Command-line
Octa is also a CLI. With no flags it launches the GUI; with one of the action flags it runs that action and exits:
octa --schema data.parquet # print column schema
octa --head data.csv -n 5 # first N rows (default 20)
octa --head data.csv -n 5 -f json # output as JSON instead of TSV
octa --convert in.csv out.parquet # convert formats
octa --sql data.parquet -q 'SELECT count(*) FROM data'
octa --export-schema data.parquet -t snowflake # schema as DDL / model / struct
Output format is selectable with -f / --format {tsv|json|csv} (TSV default). Run octa --help for the full reference, and see the CLI docs for every action.
MCP server
octa --mcp starts a Model Context Protocol server on stdio that exposes Octa's reading, inspection, analysis, and write capabilities as MCP tools, including SQL, schema export, profiling, diffing, pivot / unpivot, correlation, and directory-wide search. Point any MCP client (Claude Desktop, Claude Code,
MCP Inspector, or any compatible client) at the same binary and the model can query your local data files directly, no scripting in between. Add --mcp-read-only to drop the data-writing tools for clients that should only ever read.
Assistant (in-app chat)
A docked chat panel where you ask an LLM about your data in plain language and it answers by driving Octa's own tools against the tabs you already have open. It is the in-application sibling of the MCP server, with no external client to set up. It can save results back to a file and, when you allow it, edit the data in an open tab directly (add or drop columns, set cells, insert or delete rows). A Write protection setting (on by default) keeps the assistant read-only until you opt in, and an optional backup keeps a timestamped copy of any file the assistant changes before it is overwritten.
It is local-first and provider-agnostic: use a cloud model (Anthropic Claude, OpenAI, Google Gemini, or any OpenAI-compatible endpoint such as OpenRouter / Groq / LM Studio) or run fully offline with Ollama. API keys are kept in your OS keyring, reads are sandboxed to the files you have open, and writes go to a configurable export directory.
A Prompts library lets you save and reuse common requests, and an optional tool-call audit log records every tool the assistant runs (metadata only, never cell contents) for review. A chat session can be exported to Markdown for a readable transcript or to JSON for an exact, reusable archive.
Settings
- Configurable font size and theme
- Font picker for the SQL editor
- Performance knobs such as streaming row caps and size limits
- User-extensible "open as plain text" extension list
- Directory sidebar filter to list only files Octa can open (on by default)
- Built-in window title bar with the controls in the toolbar, drag-to-move and edge resize (on by default; turn off for native decorations)
- Remappable keyboard shortcuts
Other
- CSV delimiter auto-detection (comma, semicolon, pipe, tab) and manual selection
- Date inference for text-formatted columns (CSV, JSON, Excel, etc.) with an ambiguity picker for European vs US-format dates
- Optional debug logging and crash capture, with a one-click redacted debug report (Help -> Export debug report) to attach to bug reports
- Auto-update check from GitHub releases
- Cross-platform: Linux, macOS, and Windows
Docker / Containers
A headless container image ships the CLI actions and the --mcp server (no
GUI, since the windowing libraries are never loaded on the headless paths). It
is published to the GitHub Container Registry, so no Rust toolchain or local
build is needed:
docker pull ghcr.io/thorstenfoltz/octa:latest
Mount a data directory and pass any CLI flag (the binary is the entrypoint):
docker run --rm -v "$PWD:/data" octa --schema /data/file.parquet
Run the MCP server over stdio with an interactive stdin (-i):
docker run --rm -i -v "$PWD:/data" octa --mcp
The same Dockerfile works with Podman (swap docker for podman).
Installation
Linux (and WSL)
The quickest way to install is a single command that downloads get-octa.sh
and runs it. The script fetches the latest release from GitHub, extracts it,
and installs the binary, icon, desktop entry, and man page.
User-local install with curl (no sudo needed, installs to ~/.local):
curl -fsSL https://raw.githubusercontent.com/thorstenfoltz/octa/master/get-octa.sh | bash -s -- ~/.local
System-wide install with curl (installs to /usr/local):
curl -fsSL https://raw.githubusercontent.com/thorstenfoltz/octa/master/get-octa.sh | sudo bash
The same with wget, user-local:
wget -qO- https://raw.githubusercontent.com/thorstenfoltz/octa/master/get-octa.sh | bash -s -- ~/.local
And wget, system-wide:
wget -qO- https://raw.githubusercontent.com/thorstenfoltz/octa/master/get-octa.sh | sudo bash
After a user-local install, make sure ~/.local/bin is on your PATH (it
usually is on Ubuntu and most other distributions). If octa is not found,
add export PATH="$HOME/.local/bin:$PATH" to your ~/.bashrc or ~/.zshrc.
If octa is still "command not found" right after installing even though it
landed in a directory already on your PATH (e.g. /usr/local/bin), your
running shell has a stale command-lookup cache. Refresh it with hash -r
(bash) or rehash (zsh), or just open a new shell. That is why a shell
restart "fixes" the install.
On WSL you may see harmless warnings on launch such as:
libEGL warning: failed to get driver name for fd -1
MESA: error: ZINK: failed to choose pdev
libEGL warning: failed to create dri2 screen
These come from Mesa, not Octa: WSL has no native OpenGL GPU, so Mesa fails to initialise its Zink/DRI hardware path and falls back to software rendering (llvmpipe). Octa still runs correctly and fast. To silence the noise, force software rendering up front:
LIBGL_ALWAYS_SOFTWARE=1 octa file.parquet
# or make it permanent for the shell:
echo 'export LIBGL_ALWAYS_SOFTWARE=1' >> ~/.bashrc # or ~/.zshrc
Prefer not to pipe a script? Download the Linux tarball from the releases page and extract it:
tar xzf octa-*-linux-x86_64.tar.gz
Run the binary in place without installing:
./octa-*-linux-x86_64/octa
Or install it system-wide from the extracted directory:
sudo ./octa-*-linux-x86_64/install.sh
Linux AppImage
An AppImage is published alongside each release
for users who prefer a single portable file. Download Octa-*-x86_64.AppImage
from the releases page and
make it executable:
chmod +x Octa-*-x86_64.AppImage
Then run it directly:
./Octa-*-x86_64.AppImage
See Installation for the FUSE-less AppImage fallback and other options.
Linux (build from source)
Install the Rust toolchain from https://rustup.rs/ and the native libraries (GTK 3, fontconfig, freetype, libssl, libxcb, libxkbcommon), then clone the repository:
git clone https://github.com/thorstenfoltz/octa.git && cd octa
Build a release binary:
cargo build --release
Then install the freshly built binary with the local install.sh:
./install.sh ~/.local
Full dependency list and build notes are in the documentation.
Arch Linux
Available on the AUR as octa (build from source) and octa-bin (prebuilt binary).
Both install man octa automatically.
paru -S octa
or
paru -S octa-bin
Windows
The simplest option is to download octa.exe from the
releases page and run it
directly — no installation needed. Place it wherever you like (e.g. your
Desktop or C:\Tools\) and double-click to launch.
Optionally, install.bat copies the binary into Program Files\Octa,
generates an .ico (if ImageMagick is on PATH), and creates a Start Menu
shortcut. Right-click and choose Run as administrator. It does not
modify your PATH; open Octa via the Start Menu shortcut or by running
"C:\Program Files\Octa\octa.exe" directly.
For a no-admin install that also avoids the SmartScreen prompt, use
install.ps1 instead. It downloads the latest release, verifies its
SHA256 checksum, installs into %LOCALAPPDATA%\Programs\Octa, and unblocks
the binary so the unsigned exe launches cleanly:
powershell -ExecutionPolicy Bypass -File install.ps1
Pass -Version v1.2.3 for a specific release or -InstallDir <path> to
install elsewhere.
Windows SmartScreen warning: Octa is not code-signed, so on first launch Windows shows "Windows protected your PC". Click More info, then Run anyway. Subsequent launches open without the prompt.
macOS
The simplest option is to download the macOS .app bundle from the
releases page. The build
targets Apple Silicon (aarch64) Macs. Drop Octa.app into
/Applications (or anywhere else) and double-click to launch.
First-launch unsigned-app warning: Octa is not code-signed or notarized, so macOS quarantines the app the first time you launch it. You'll see "Octa.app cannot be opened because the developer cannot be verified". Two ways around it:
- Right-click the app icon in Finder, choose Open, then click Open in the confirmation dialog. macOS remembers the choice for that copy of the app.
- Or remove the quarantine attribute from a terminal:
# Locate the bundle and confirm its quarantine attribute is present
find /Applications -maxdepth 1 -name "Octa.app" -exec xattr {} \;
# Strip the attribute (top-level only — macOS only quarantines the bundle)
xattr -d com.apple.quarantine /Applications/Octa.app
# If the strip above fails with "No such xattr: …" but the warning persists,
# fall back to the recursive form once (handles "Octa.app is damaged"):
# xattr -cr /Applications/Octa.app
To build from source, install the Rust toolchain (https://rustup.rs/)
and the native dependencies via Homebrew, then cargo build --release:
brew install harfbuzz freetype gtk+3
cargo build --release
The resulting binary lives at target/release/octa.
Configuration
Settings are stored in:
- Linux:
$XDG_CONFIG_HOME/octa/settings.toml(defaults to~/.config/octa/settings.toml) - macOS:
~/Library/Application Support/Octa/settings.toml - Windows:
%APPDATA%\Octa\settings.toml
License
MIT
No comments yet
Be the first to share your take.