airplan

command module
v0.12.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 7, 2026 License: MIT Imports: 2 Imported by: 0

README

Airplan logo

airplan

Turn local documents and artifacts into shareable links.

GitHub Release Go Reference GitHub Issues GitHub Pull Requests License

airplan uploads documents and file collections to S3-compatible storage and prints unguessable URLs:

$ airplan plan.md
https://plans.example.com/vq3nhk2p7r4wzt5c6ydjm3xhqd/plan.html

It is especially useful with coding agents. An agent can turn a local plan into a link you can open from any device, even when the agent runs elsewhere and its local files are hard to reach. It also works whenever you want to share a local document without running a server or using a paste service.

Airplan handles several kinds of input:

  • Markdown becomes a polished page with eleven built-in themes, independent light and dark mode themes, and validated custom themes. Authored HTML and link destinations are preserved, so treat the input as trusted content.
  • Source and plain-text files become highlighted, gist-like pages.
  • A primary Markdown file and its supporting pages and assets become one linked document bundle.
  • HTML stays HTML, with no rendering step. It may execute scripts when someone opens the link, so treat it as trusted code.
  • Images, video, audio, PDFs, archives, and other peer artifacts become a file collection with an overview page.
  • Files live in a bucket you own. The CLI can call S3 directly, or clients can use one single-user Airplan server that holds the storage credentials.
  • stdout has a stable contract for scripts and agents. URLs or one JSON object go to stdout; logs and errors go to stderr.

SPEC.md defines the exact behavior.

Live examples

  • Zero-downtime token migration is a realistic Markdown implementation plan with a diagram, responsive columns, alerts, tables, task lists, highlighted code, and automatic GitHub issue and pull-request links.
  • How airplan works is a concise architecture overview showing the CLI and library workflows.
  • Upload with airplan's Go API is a runnable Go example presented as a highlighted, gist-like page.
  • API rollout handoff is a document bundle with linked Markdown and Go pages, page navigation, and an SVG request-flow asset.
  • Release verification evidence is a file collection with an image preview and direct links to every original artifact.

Install

On macOS, install the signed and notarized release with Homebrew:

brew install --cask jimeh/tap/airplan

On Linux, macOS, or Windows, mise can install the matching release binary:

mise use -g github:jimeh/airplan

If you have Go, build the latest release from source:

go install github.com/jimeh/airplan@latest

Building requires the Go version declared in go.mod. Locally built binaries are not signed by the project. You can also download prebuilt Linux, macOS, and Windows binaries from GitHub Releases.

See Verify a release for checksums, signatures, attestations, SBOMs, and reproducible container references.

Quick start

The default s3 backend calls storage directly from the CLI. You need an S3-compatible bucket, a public base URL, and credentials scoped to that bucket. Uploads require object-write access. Commands that inspect or delete remote uploads also need object-read, bucket-list, or object-delete access as relevant.

If several machines should share one storage configuration, follow Run a single-user Airplan server instead.

Create a Cloudflare R2 bucket

Cloudflare R2 provides an S3-compatible API and custom domains for public links. To use it:

  1. Create a bucket, such as plans, in R2 > Create bucket.
  2. Under Bucket > Settings > Custom Domains, connect a domain such as plans.example.com.
  3. Under R2 > Manage API Tokens, create an Object Read & Write token scoped to this bucket. Do not use account-level or admin credentials.
  4. Record the S3 endpoint, bucket name, custom domain, access key ID, and secret access key for the next step.

The custom domain should serve uploaded objects without exposing a public bucket listing. As a quick check, its root URL should return an error while a known object URL loads normally.

Another S3-compatible service works too. Substitute its endpoint, region, bucket, credentials, and public URL in the same configuration.

Configure airplan

Create ~/.config/airplan/config.toml on Linux or macOS. Windows uses its normal per-user configuration directory.

#:schema https://github.com/jimeh/airplan/releases/latest/download/airplan.schema.json
backend           = "s3"
endpoint          = "https://<account-id>.r2.cloudflarestorage.com"
bucket            = "plans"
region            = "auto"
public_base_url   = "https://plans.example.com"
light_theme       = "github-light"
dark_theme        = "github-dark"
access_key_id     = "..." # or AIRPLAN_ACCESS_KEY_ID
secret_access_key = "..." # or AIRPLAN_SECRET_ACCESS_KEY

Replace the placeholders with your storage values. Explicit access and secret keys must be configured as a pair. Omit both to use the standard AWS credential chain.

If the file contains credentials, protect it with chmod 600. Airplan warns when its permissions are too broad. The #:schema comment enables validation and completion in editors with Taplo or the Even Better TOML extension.

Run airplan config show to inspect the resolved values and their sources. Credentials are redacted. The configuration guide covers profiles, environment variables, themes, precedence, and diagnostics.

Upload a document

Pass Airplan a file and use the URL it prints:

$ airplan plan.md
https://plans.example.com/vq3nhk2p7r4wzt5c6ydjm3xhqd/plan.html

Add --open to open the result in a browser, or --json for structured output:

airplan --open plan.md
airplan --json plan.md

Share documents and files

Upload one document

Airplan infers the document format from its name and content:

airplan plan.md                # Markdown page
airplan report.html            # authored HTML
airplan pkg/server/handler.go  # highlighted source page

Standard input defaults to Markdown when no format can be inferred:

cat plan.md | airplan --slug my-plan -
cat main.go | airplan --format txt --lang go -
Upload a document bundle

Pass a primary Markdown document with its supporting pages and assets:

airplan README.md docs/design.md examples/server.go \
  images/flow.svg recordings/demo.mp4

Airplan prefers a root README.md, then index.md, then the first Markdown file as the entry. --entrypoint PATH overrides that choice. Other Markdown and UTF-8 source files become linked pages. HTML, media, and opaque resources become byte-exact assets. Use --page or --asset when an inferred role is wrong.

The entry file's directory is the bundle root, and every path must remain beneath it after Airplan resolves symlinks. Assets keep their nested paths and bytes. The plain result remains the entry URL. JSON adds ordered .pages and .assets arrays.

Upload a file collection

Multiple files without Markdown or HTML create a collection. Airplan prints each direct file URL in argument order, then the overview URL:

$ airplan login.png settings.png demo.webm
https://plans.example.com/vq3n.../login.png
https://plans.example.com/vq3n.../settings.png
https://plans.example.com/vq3n.../demo.webm
https://plans.example.com/vq3n.../index.html

A single recognized media or binary file also becomes a collection. Use --collection when text-like input should be uploaded unchanged:

airplan screenshot.png
airplan --collection README.md
airplan --json screenshot.png recording.webm

Collection JSON retains .url for the overview and puts ordered direct links in .files[].url. Airplan does not change the original file bytes.

Preview without uploading

preview uses the same renderer locally. It does not need storage credentials, contact S3, or update upload history.

airplan preview plan.md > plan.html
airplan preview -o plan.html plan.md
airplan preview README.md docs/design.md images/flow.svg \
  --output-dir ./preview
airplan preview --collection screenshot.png demo.webm -o index.html

A preview with supporting pages or assets requires --output-dir. Airplan refuses an existing directory so an interrupted preview cannot look complete.

Manage uploads

Airplan records successful uploads in the platform state directory. On Linux and macOS the default is ~/.local/state/airplan/manifest.jsonl; Windows uses %LocalAppData%\airplan\manifest.jsonl. Override it with --manifest PATH or AIRPLAN_MANIFEST.

Common lifecycle commands are:

airplan list                         # local upload history
airplan list --remote                # uploads found in the bucket
airplan show <url-or-key>            # inspect one remote upload
airplan get <url-or-key>             # fetch its entry page
airplan protect <url-or-key>         # exclude it from purge
airplan delete <url-or-key>          # delete one upload
airplan purge --older-than 30d       # review and delete older uploads
airplan sync                         # reconcile remote and local history
airplan upgrade --check <url-or-key> # inspect a Markdown upgrade
airplan new-revision <url-or-key> plan.md

new-revision preserves every revision's source, URL, and numbered identity. The renderer may still be upgraded in place without changing those values.

See Manage uploads for filtering, output columns, remote discovery, synchronization, protection, Markdown upgrades, and linked revisions.

Pages Airplan creates

Markdown pages include per-theme syntax highlighting, Mermaid diagrams from mermaid fences, a responsive table of contents, GitHub-style alerts, definition lists, YAML and TOML frontmatter, responsive Pandoc columns, rendered and source views, copy buttons, and links to the original Markdown. Frontmatter appears collapsed at the top. Its string title sets the page title unless --title is given. Use --no-source if the original should not be uploaded.

By default, Markdown references such as #123, owner/repo#456, and full commit IDs link against a locally discovered GitHub origin. Use --repo none to disable this or --repo https://github.example/owner/repo to supply explicit GitHub Enterprise-compatible context. Discovery is local and does not contact the remote.

Everything except the optional Mermaid runtime is embedded in the HTML. Use --no-external-assets to keep Airplan-managed features offline, or --mermaid-url to select another HTTPS CDN or self-hosted module. This setting does not block external content authored in trusted Markdown, HTML, or custom templates.

Multi-page documents add a Pages rail, active-page indicator, previous and next links, and a separate On this page rail. Each page remains a standalone HTML document with ordinary browser navigation.

Collection overview pages render images inline, video and audio with controls, and arbitrary files as linked cards. Members have Open, Download, and Copy URL actions. Media never autoplays, images lazy-load, and links work without JavaScript.

Automation and agents

For upload commands, stdout is safe to capture:

  • A successful document prints its page URL and nothing else.
  • A successful collection prints ordered direct URLs followed by its overview.
  • With --json, stdout contains one JSON object instead.
  • Logs, warnings, progress, and errors go to stderr.
  • A non-zero exit means no upload URL was printed.
url=$(airplan plan.md)
url=$(airplan --json plan.md | jq -r .url)
overview=$(airplan --json screenshot.png demo.webm | jq -r .url)
image=$(airplan --json screenshot.png | jq -r '.files[0].url')

Do not invent or reuse a URL after a failed command.

Install the agent skill

Install and configure the Airplan CLI on the machine where the agent runs, then install the Airplan agent skill. The skill teaches compatible coding agents to upload requested documents and file sets, including visual evidence from authorized pull-request or issue work.

Install it globally with the Skills CLI:

npx skills add jimeh/airplan --skill airplan --global

The binary also contains the matching skill. An agent with the CLI can inspect or install that copy directly:

airplan skill

mkdir -p ~/.agents/skills/airplan
airplan skill > ~/.agents/skills/airplan/SKILL.md

Once configured, ask the agent to share a plan, screenshot, or recording as a link.

Connect through MCP

For a local agent, add airplan mcp as a stdio server. It follows the selected s3 or airplan profile exactly like the CLI. Remote agents can connect to a running Airplan server at https://airplan.example.com/mcp with an Authorization: Bearer <token> header.

MCP exposes document and collection uploads, revisions, listing, inspection, deletion, synchronization, two-phase purge, and preview-first upgrades. Hosted MCP does not accept server-local paths. See the server guide for the transport and trust boundaries.

Configuration

Airplan resolves settings from command-line flags, AIRPLAN_* environment variables, a named profile, root-level config values, then built-in defaults. Use profiles when several buckets or an Airplan server share one config file:

backend = "s3"
endpoint = "https://<account-id>.r2.cloudflarestorage.com"
region = "auto"
default_profile = "work"

[profiles.work]
bucket = "work-plans"
public_base_url = "https://plans.work.example.com"

[profiles.shared]
backend = "airplan"
api_url = "https://airplan.example.com"
api_token = "..."

Select a profile with --profile NAME, -p NAME, or AIRPLAN_PROFILE. airplan config profiles lists configured profiles, airplan config show explains the resolved values, and airplan config schema prints the complete JSON Schema.

The built-in appearance control keeps System, Light, and Dark mode separate from the theme assigned to each mode. Airplan includes GitHub, Catppuccin, Rose Pine, Solarized, Tokyo Night, and One Dark themes. You can limit that catalog or define custom themes in the config file.

The configuration guide documents every configuration source, backend rule, theme setting, environment variable, and shell completion command.

Privacy model

Airplan links are capability URLs. Every path contains 128 random bits. By default, generated pages include a noindex directive. --indexable or indexable = true removes that directive. The link is effectively private while it remains unknown, but it is not access-controlled:

  • Anyone with the link can open it and pass it on.
  • Chat tools may scan or prefetch links shared through them.
  • Objects remain in the bucket until they are deleted. Airplan serves them with Cache-Control: no-store so browsers and shared caches should not retain a reusable response after deletion.
  • Bucket listing must remain private.

Review screenshots and recordings for tokens, usernames, private messages, browser chrome, and unrelated desktop content before uploading. Filenames are public in direct URLs and collection overviews.

Airplan does not sanitize authored HTML or byte-exact assets. HTML, SVG, and JavaScript may execute when opened or embedded. Every nested page and asset URL shares the entry's capability boundary and persists until the owning document is deleted.

Use airplan purge --older-than 30d --yes manually or from cron when uploads should expire. Protect an upload that must survive bulk cleanup:

airplan protect --reason "linked from project README" <url-or-key>

Protection is enforced by compatible Airplan clients, not by storage. Older Airplan builds and native storage tools can delete protected uploads.

Go library

The CLI is a thin shell over the importable github.com/jimeh/airplan/airplan package. With ctx and an open io.Reader named file:

cfg, err := airplan.LoadConfig(airplan.ConfigOptions{})
if err != nil {
    return err
}

client, err := airplan.New(ctx, cfg)
if err != nil {
    return err
}

result, err := client.Upload(ctx, airplan.Input{
    Reader: file,
    Name:   "plan.md",
})
if err != nil {
    return err
}

fmt.Println(result.URL)

Use UploadDocument for linked pages and assets, UploadFiles for collections, and CreateDocumentRevision for replacement revisions. RenderDocument renders a bundle without storage. See Use Airplan as a Go library for examples and API boundaries.

Development

The project uses mise for its task commands:

mise run treeboot           # bootstrap a new linked worktree
mise run setup              # install tools and Git hooks
mise run check              # lint, generated files, format, and unit tests
mise run test-integration   # MinIO round trip; requires Docker
mise run test:browser       # Chromium page smoke tests
mise run audit:deps         # audit Go modules and Bun dependencies
mise run release:snapshot   # build release artifacts without publishing
mise run verify             # broad local validation

Run mise run check before handing work off. Use mise run verify for broad or risky changes. AGENTS.md contains the repository map, full task list, testing guidance, and contribution constraints. IMPLEMENTATION.md describes the architecture.

Releases use release-please and GoReleaser. Pull-request titles follow Conventional Commits because they determine release versions and changelog entries.

License

MIT. See LICENSE.

Documentation

Overview

Command airplan renders and uploads AI/LLM plan documents, with a local preview path that does not require object storage. Behavior is defined by SPEC.md.

Directories

Path Synopsis
Package airplan is the core library behind the airplan CLI: config loading, input detection, markdown rendering, key generation, and uploads to S3-compatible storage.
Package airplan is the core library behind the airplan CLI: config loading, input detection, markdown rendering, key generation, and uploads to S3-compatible storage.
Package api exposes Airplan's authoritative REST wire contract.
Package api exposes Airplan's authoritative REST wire contract.
Package cli implements the airplan command-line interface.
Package cli implements the airplan command-line interface.
internal
cmd/checkopenapi command
Command checkopenapi verifies that the checked-in OpenAPI-generated Go client/server/models exactly match api/openapi.yaml.
Command checkopenapi verifies that the checked-in OpenAPI-generated Go client/server/models exactly match api/openapi.yaml.
cmd/checkspecsync command
Command checkspecsync detects contract changes that are missing the corresponding specification version updates.
Command checkspecsync detects contract changes that are missing the corresponding specification version updates.
cmd/genmermaid command
Command genmermaid generates the pinned Mermaid dependency constants.
Command genmermaid generates the pinned Mermaid dependency constants.
cmd/genschema command
cmd/preparecontainer command
Command preparecontainer creates a deterministic Docker build context from GoReleaser artifact metadata.
Command preparecontainer creates a deterministic Docker build context from GoReleaser artifact metadata.
cmd/updatedemos command
Command updatedemos refreshes README demo links when any published object in origin storage differs from the repository's upload-mode fixtures.
Command updatedemos refreshes README demo links when any published object in origin storage differs from the repository's upload-mode fixtures.
cmd/updatemermaid command
Command updatemermaid safely updates the repository's Mermaid pin.
Command updatemermaid safely updates the repository's Mermaid pin.
httpapi/generated
Package generated provides primitives to interact with the openapi HTTP API.
Package generated provides primitives to interact with the openapi HTTP API.
serverlog
Package serverlog provides the deliberately narrow logging surface used by the self-hosted HTTP server.
Package serverlog provides the deliberately narrow logging surface used by the self-hosted HTTP server.
Package skills embeds the canonical agent skills shipped with airplan.
Package skills embeds the canonical agent skills shipped with airplan.
tests
browser/fixture command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL