Overview
aktar is a remote control for the Aktar app on the same computer. You hand it files, it passes them to Aktar on 127.0.0.1, and Aktar uploads them with the destinations, keys, path templates and link settings you already set up. The link comes back to your terminal.
What it is
- A small Node.js command for scripts, CI jobs on your own Mac or Windows machine, and editors like Typora
- A way to upload files and the clipboard, show QR codes, and read your destinations and upload history
- Free and open source (MIT), like the app
What it isn't
- Not standalone: without Aktar running, it can't upload anything. It never holds your storage keys
- Not for Linux servers or remote machines: it only talks to Aktar on the same computer. There, use your storage provider's own CLI
Requirements
- Aktar for Mac 0.4.0 or later, or Aktar for Windows, with at least one destination
- Node.js 18 or later (Homebrew installs it for you)
-
--name,--qrandaktar qrneed aktar 0.2.0 or later - Reusing the link of a file that's already uploaded needs aktar 0.2.0 and Aktar for Mac 0.10.0 or Aktar for Windows 0.3.0
Install
Install it once, with npm or Homebrew. Both put the aktar command on your path.
With npm (macOS and Windows)
npm install -g @getaktar/cliWith Homebrew (macOS)
brew install getaktar/tap/aktar-cliOr run it once without installing anything:
npx @getaktar/cli upload file.pngCheck which version you have:
aktar --version
0.2.0Connect to Aktar
- In Aktar for Mac or Windows, open Settings > Integrations and turn on Allow local connections. Aktar then listens on
127.0.0.1only, port 47913 unless you change it there. - Copy the token shown under it.
- Run
aktar loginand paste the token at theToken:prompt. It stays hidden while you type, and it's checked against Aktar before it's saved, so a typo isn't stored.
aktar login
Copy the token from Aktar: Settings > Integrations (turn on Allow local connections first).
Token:
Connected to Aktar 0.10.0. Saved to /Users/you/.config/aktar/cli.jsonWhere the token is kept
aktar login saves the token and port in ~/.config/aktar/cli.json on a Mac (or $XDG_CONFIG_HOME/aktar/cli.json if you set it) and in %APPDATA%\aktar\cli.json on Windows. Only your user can read it.
{
"token": "…",
"port": 47913
}aktar logout deletes that file. To lock out every client at once, click Regenerate next to the token in Aktar; then run aktar login again with the new one.
Scripts and CI
On a build machine, skip aktar login and set AKTAR_TOKEN (and AKTAR_PORT if you changed the port). They win over the saved file. Aktar has to be running on that machine, signed in to the same user session.
AKTAR_TOKEN="$AKTAR_SECRET" aktar upload dist/app.zip -d Builds| Variable | What it does |
|---|---|
AKTAR_TOKEN | The token from Aktar's Settings > Integrations. Used instead of the saved one. |
AKTAR_PORT | Aktar's local API port, if you changed it. --port wins over it. |
NO_COLOR | Draws QR codes with plain characters instead of terminal colors. |
XDG_CONFIG_HOME | Where the config folder is on macOS (default ~/.config). On Windows, %APPDATA% is used. |
Commands
All commands talk to Aktar, except aktar qr with plain text or a link. Without -d, uploads go to the destination selected in Aktar's menu bar, and they show up in Aktar's history like any other. aktar --help prints the same list.
aktar uploadaktar upload --clipboardaktar qraktar loginaktar logoutaktar statusaktar destinationsaktar history
aktar upload
Uploads one or more files and prints one link per file, in order, as each one finishes.
Usage
aktar upload <file>... [options]Options
| Flag | Value | Description |
|---|---|---|
-d, --destination | <name|id> | Destination to upload to, by name or ID. Default: the one selected in Aktar. |
-f, --format | <format> | What to print: url (default), markdown, html or custom (your template in Aktar). |
--name | <name> | Upload one file under this name. Its extension is kept if the name has none. |
--folder | <path> | Keep the file name and upload into this folder. |
--expires | <days> | Let the bucket delete the file after 1, 7, 14 or 30 days. 0 keeps it. |
--qr | Also print a QR code of each link. | |
--json | Print a JSON array with the full details of each upload, instead of links. | |
--port | <port> | Aktar's local API port, for this command only. Wins over AKTAR_PORT and the saved port. |
Good to know
- Every file is checked before anything is uploaded: if one doesn't exist, nothing is uploaded and
aktarexits with code 2. - If one upload fails, the others still go through. Each failure is reported on stderr, and the exit code is 1.
- Without
--folder, the destination's path template names the file, just like a drop on the menu bar. With--folder, the file keeps its name inside that folder. --nameworks with one file at a time. Slashes are removed, and the file's extension is added if the name has none:--name coveruploadsIMG_4021.jpgascover.jpg. With--folder, it's the name inside the folder.--expirestakes 1, 7, 14 or 30 (days), or 0 to keep the file. Without it, the file is kept, whatever Delete after is set to in Aktar. It needs Aktar's auto-delete rules on that destination, and it can't be combined with--folder.--qrcan't be combined with--json, and--pngonly works withaktar qr.- In a terminal, progress is shown on stderr while a file uploads. The file is streamed to Aktar, so its size doesn't matter, and there's no time limit.
Examples
aktar upload screenshot.png
https://files.example.com/2026/10/7f3c2a91.pngaktar upload *.png -f markdown

aktar upload IMG_4021.jpg --name cover --folder blog/2026
https://files.example.com/blog/2026/cover.jpgaktar upload photo.jpg
aktar: photo.jpg: already uploaded, reused the existing link
https://files.example.com/2026/09/4d2a8c61.jpgaktar upload --clipboard
Uploads the file or image that's on the clipboard, like Aktar's own clipboard shortcut.
Usage
aktar upload --clipboard [options]Options
| Flag | Value | Description |
|---|---|---|
--clipboard | Upload what's on the clipboard instead of files. | |
-d, --destination | <name|id> | Destination to upload to, by name or ID. Default: the one selected in Aktar. |
-f, --format | <format> | What to print: url (default), markdown, html or custom (your template in Aktar). |
--expires | <days> | Let the bucket delete the file after 1, 7, 14 or 30 days. 0 keeps it. |
--qr | Also print a QR code of each link. | |
--json | Print a JSON array with the full details of each upload, instead of links. | |
--port | <port> | Aktar's local API port, for this command only. Wins over AKTAR_PORT and the saved port. |
Good to know
- Pass files or
--clipboard, not both. --nameand--folderonly work with files.-d,-f,--expires,--qrand--jsonwork as with files.- With
--json, it still prints an array, with one entry.
Examples
aktar upload --clipboard
https://files.example.com/2026/10/9a4c03be.pngaktar upload --clipboard -f markdown -d Blog
aktar upload --clipboard --expires 1
https://files.example.com/tmp/1d/2026/10/38f0d7c2.pngaktar qr
Shows a QR code of a link or any text in the terminal, so you can open it on a phone. Given an upload ID, it shows that upload's link and its QR code.
Usage
aktar qr <link|text|upload-id> [--png <file>]Options
| Flag | Value | Description |
|---|---|---|
--png | <file> | Save the QR code as a PNG file instead of printing it. |
--port | <port> | Aktar's local API port, for this command only. Wins over AKTAR_PORT and the saved port. |
Good to know
- Pass exactly one argument, and quote text with spaces.
- An upload ID is the
idfromaktar history --jsonoraktar upload --json.aktarlooks it up in the last 1,000 uploads in Aktar's history; for plain text or a link, it doesn't need Aktar at all. - The code is drawn black on white in a terminal, whatever its theme. When the output isn't a terminal, or
NO_COLORis set, it's drawn without colors. --pngsaves the QR code as a PNG instead of printing it.
Examples
aktar qr https://files.example.com/2026/10/demo.mp4aktar qr 0B6C2F8E-3D1A-4C55-9E2B-7A41D0C3E9F1 --png cover-qr.png
Saved to cover-qr.pngaktar login
Checks a token against Aktar and saves it, so the other commands can connect.
Usage
aktar login [--token <token>] [--port <port>]Options
| Flag | Value | Description |
|---|---|---|
--token | <token> | The token, instead of the prompt. |
--port | <port> | Aktar's port, saved along with the token. Default 47913. |
Good to know
- In a terminal, it asks for the token and hides what you type. When the input is piped, it reads the token from there.
--tokenskips the prompt, but the token ends up in your shell history. Piping it in orAKTAR_TOKENavoids that.- A token Aktar rejects isn't saved, and the exit code is 3.
Examples
aktar login
Copy the token from Aktar: Settings > Integrations (turn on Allow local connections first).
Token:
Connected to Aktar 0.10.0. Saved to /Users/you/.config/aktar/cli.jsonpbpaste | aktar login
Connected to Aktar 0.10.0. Saved to /Users/you/.config/aktar/cli.jsonaktar login --token "$AKTAR_SECRET" --port 47920
Connected to Aktar 0.10.0. Saved to /Users/you/.config/aktar/cli.jsonaktar logout
Deletes the saved token and port.
Usage
aktar logoutOptions
No options.
Good to know
AKTAR_TOKEN, if set, still works afteraktar logout.- It doesn't disconnect other clients. For that, regenerate the token in Aktar.
Examples
aktar logout
Logged out.aktar status
aktar: Not connected to Aktar yet. Run aktar login with the token from Aktar's Settings > Integrations.aktar status
Shows which Aktar you're connected to, on which port, the destination selected in Aktar, and where the config file is.
Usage
aktar status [--json]Options
| Flag | Value | Description |
|---|---|---|
--json | Print JSON instead of text. | |
--port | <port> | Aktar's local API port, for this command only. Wins over AKTAR_PORT and the saved port. |
Good to know
- A quick way to check the connection in a script: it exits with code 3 when Aktar can't be reached or the token is wrong.
Examples
aktar status
Aktar 0.10.0 (build 28) on port 47913
Destination: Screenshots (Cloudflare R2, screenshots)
Config: /Users/you/.config/aktar/cli.jsonaktar status --json
{
"app": "Aktar",
"version": "0.10.0",
"build": "28",
"apiVersion": 1,
"defaultDestinationId": "5D1A9C3E-7B2F-4E61-8A0D-3C9B6F2E1A47",
"outputFormat": "url",
"port": 47913
}aktar destinations
Lists your destinations with their provider, bucket and ID. * marks the one selected in Aktar.
Usage
aktar destinations [--json]Options
| Flag | Value | Description |
|---|---|---|
--json | Print JSON instead of text. | |
--port | <port> | Aktar's local API port, for this command only. Wins over AKTAR_PORT and the saved port. |
Good to know
- Use a name or an ID with
-d. Names are matched without regard to case; an ID always works, even after a rename. --jsongivesid,name,provider,providerName,bucket,publicBaseURLandisDefaultfor each.
Examples
aktar destinations
* Screenshots Cloudflare R2 screenshots 5D1A9C3E-7B2F-4E61-8A0D-3C9B6F2E1A47
Builds Amazon S3 acme-builds A83F2C10-6D4B-4F7E-9C1A-2B5E8D0F3C66
Blog Backblaze B2 blog-assets E2C7B9A4-1F3D-4A8E-B6C0-9D5F2A7E4B13aktar destinations --json | jq -r '.[] | select(.isDefault) | .name'
Screenshotsaktar history
Lists recent uploads, newest first: date, file name and link. Words after history search the history.
Usage
aktar history [search] [options]Options
| Flag | Value | Description |
|---|---|---|
-n, --limit | <n> | How many uploads to list. Default 20. |
-d, --destination | <name|id> | Only uploads to this destination, by name or ID. |
--json | Print JSON instead of text. | |
--port | <port> | Aktar's local API port, for this command only. Wins over AKTAR_PORT and the saved port. |
Good to know
- It lists 20 uploads unless you pass
-n. --jsongives the same fields asaktar upload --json, including each upload'sidforaktar qr.
Examples
aktar history -n 3
2026-10-01 screenshot.png https://files.example.com/2026/10/7f3c2a91.png
2026-10-01 cover.jpg https://files.example.com/blog/2026/cover.jpg
2026-09-30 invoice-september.pdf https://files.example.com/2026/09/0e7d51b3.pdfaktar history invoice -d Screenshots
2026-09-30 invoice-september.pdf https://files.example.com/2026/09/0e7d51b3.pdf
2026-08-29 invoice-august.pdf https://files.example.com/2026/08/a6c94f20.pdfaktar history -d Builds -n 1 --json | jq -r '.[0].url'
https://acme-builds.s3.amazonaws.com/2026/10/app-3f9c2e1.zipOptions for every command
| Flag | Value | Description |
|---|---|---|
-h, --help | Show the list of commands and options. | |
-v, --version | Show the version of aktar. | |
--port | <port> | Aktar's local API port, for this command only. Wins over AKTAR_PORT and the saved port. |
Output
Formats
-f picks what's printed for each upload. Without it, aktar prints the plain URL, whatever format Aktar's menu bar copies. Markdown and HTML embed images and link everything else, the same way Aktar does.
| Format | Printed for photo.jpg |
|---|---|
-f url | https://files.example.com/2026/10/7f3c2a91.jpgThe link (default). |
-f markdown | A Markdown image, or a Markdown link for other files. |
-f html | <img src="https://files.example.com/2026/10/7f3c2a91.jpg" alt="">An <img> tag, or an <a> link for other files. |
-f custom | Your template from Aktar's settings, with {url}, {filename} and the other variables filled in. |
stdout and stderr
Standard output gets only the links, one per line, in the order of the files (and the QR codes, with --qr). Everything else goes to standard error: progress, errors, and notes such as aktar: photo.jpg: already uploaded, reused the existing link. So url=$(aktar upload file) and aktar upload *.png > links.txt always get clean links.
aktar upload photo.jpg
aktar: photo.jpg: already uploaded, reused the existing link
https://files.example.com/2026/09/4d2a8c61.jpgJSON
--json prints an array with one entry per uploaded file, after all of them are done. Failed uploads are left out (they're reported on stderr), so check the exit code too.
aktar upload photo.jpg --json
[
{
"id": "0B6C2F8E-3D1A-4C55-9E2B-7A41D0C3E9F1",
"filename": "photo.jpg",
"objectKey": "2026/10/7f3c2a91.jpg",
"url": "https://files.example.com/2026/10/7f3c2a91.jpg",
"destinationId": "5D1A9C3E-7B2F-4E61-8A0D-3C9B6F2E1A47",
"destinationName": "Screenshots",
"mimeType": "image/jpeg",
"size": 482113,
"createdAt": "2026-10-01T12:00:00Z",
"expiresAt": null,
"formats": {
"url": "https://files.example.com/2026/10/7f3c2a91.jpg",
"markdown": "",
"html": "<img src=\"https://files.example.com/2026/10/7f3c2a91.jpg\" alt=\"\">",
"custom": ""
},
"reused": false
}
]| Field | Meaning |
|---|---|
id | The upload's ID in Aktar's history. aktar qr takes it. |
filename | The file name Aktar knows it by: the file's own, or --name. |
objectKey | Where the file is in the bucket. |
url | The link. |
destinationId | The destination's ID. |
destinationName | The destination's name. |
mimeType | The file's type, after any conversion by Aktar. |
size | Size in bytes, as stored. |
createdAt | When it was uploaded (ISO 8601, UTC). |
expiresAt | When the bucket deletes it, or null if it's kept. |
formats | The link in all four formats: url, markdown, html and custom. |
reused | true when nothing was uploaded because the same file was already there. Left out by Aktar versions before Mac 0.10.0 and Windows 0.3.0. |
Exit codes
| Code | Meaning |
|---|---|
0 | Everything worked. |
1 | At least one upload or request failed; the others still went through. Aktar's own errors, such as auto-delete not being set up, also end here. |
2 | Bad arguments, a file that doesn't exist, or a destination name Aktar doesn't have. Nothing was uploaded. |
3 | Not logged in, Aktar isn't running, its local API is off, or the token is wrong. |
Recipes
Copy, adjust the file names, done. Every recipe uses only what's documented above.
Keep the link in a variable
Only the link reaches standard output, so command substitution gets exactly the URL. || exit 1 stops the script if the upload failed.
url=$(aktar upload build/report.pdf) || exit 1
echo "Report: $url"
Report: https://files.example.com/2026/10/0e7d51b3.pdfUpload a build and post the link
Zip a build, upload it to a destination named Builds under the commit's short hash, and post the link to any webhook that takes JSON (a chat channel, an issue tracker, your own endpoint). set -e stops before posting if the upload fails.
#!/bin/sh
set -e
zip -qr app.zip dist
url=$(aktar upload app.zip -d Builds --name "app-$(git rev-parse --short HEAD)")
curl -fsS -X POST "$WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "{\"text\": \"New build: $url\"}"A Makefile target
make share uploads the report under today's date and keeps the link in .last-link. In a Makefile, $$ passes a $ to the shell, and the command line starts with a tab.
share: dist/report.pdf
aktar upload dist/report.pdf --name "report-$$(date +%F)" | tee .last-linkTypora image uploader
In Typora, open Settings > Image, choose Custom Command as the image uploader and enter the full path to aktar (the one which aktar prints) followed by upload. Typora passes the image paths and reads one link per line.
/opt/homebrew/bin/aktar uploadStep by step, with the When Insert… setting: Upload Typora images to S3.
Upload from Finder or Shortcuts
On a Mac, add a Run Shell Script action to a Shortcut, or to an Automator Quick Action that receives files in Finder, and set it to pass input as arguments. Shortcuts and Automator don't load your shell's PATH, hence the first line. Select files, run it, and the links are on the clipboard.
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
aktar upload "$@" | pbcopy
osascript -e 'display notification "Link copied" with title "aktar"'It uploads files, not folders. Several files give several links, one per line.
Temporary files
For a log, a screen recording or anything that shouldn't stay around, let the bucket delete it. The destination needs Aktar's auto-delete rules first: set them up once from Delete after in Aktar's menu bar or the destination's settings.
aktar upload crash.log --expires 1
aktar upload screen-recording.mov --expires 7 --qrExpiring files go under tmp/1d/, tmp/7d/ and so on in the bucket, and the bucket deletes them even when Aktar isn't running.
Open a link on your phone
--qr prints a QR code under the link; point your phone's camera at the terminal. To show it again later, pass an upload's ID to aktar qr, here the latest upload matching "demo".
aktar upload demo.mp4 --qr
aktar qr "$(aktar history demo -n 1 --json | jq -r '.[0].id')"Windows PowerShell
The same works in PowerShell. $LASTEXITCODE holds the exit code, and ConvertFrom-Json reads --json.
$url = aktar upload .\dist\app.zip -d Builds
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Set-Clipboard $url
$upload = aktar upload .\report.pdf --json | Out-String | ConvertFrom-Json
$upload.formats.markdownWhat Aktar does for you
The CLI only passes files on; Aktar does the rest, with each destination's settings. So everything you set up in the app applies to aktar upload too, without any extra options, and the link you get is the one for the file as stored.
-
Path templates
The destination's object path names the file, with
{year},{uuid},{filename}and the rest.{md5}and{sha256}name files by their contents. -
Image processing
If the destination converts images to WebP or AVIF, compresses or resizes them, CLI uploads get the same treatment, and the link points to the converted file.
-
Photo metadata
Image metadata in the destination's upload defaults removes the location from photos, or all metadata, before they leave your computer.
-
Same file, same link
With Reuse links for duplicate files on, the same contents in the same destination get the existing link instead of a second upload.
aktarnotes it on stderr. -
Big files
Large files go up in parts, with no 5 GB limit, are retried after a dropped connection and resume where they stopped.
aktarstreams the file to Aktar, so it never loads it into memory. -
Public or temporary links
Whether the link is public or a temporary (presigned) one that also works for a private bucket is the destination's choice, as in the app.
Duplicate links, image processing, {md5} and {sha256} and uploads in parts came with Aktar for Mac 0.10.0 and Windows 0.3.0: what's new in 0.10.0.
Troubleshooting
Error messages from aktar start with aktar: and go to stderr. The exit code tells you what kind of problem it is.
Aktar isn't running, or its local API is turned off
Open Aktar and check that Allow local connections is on in Settings > Integrations. If you changed the port there, pass it with --port or AKTAR_PORT, or run aktar login --port again. Exit code 3.
Not connected to Aktar yet
There's no saved token and no AKTAR_TOKEN. Run aktar login with the token from Aktar's Settings > Integrations. Exit code 3.
The token doesn't match Aktar's anymore
The token was regenerated in Aktar, or it's mistyped. Run aktar login again with the current one. If AKTAR_TOKEN is set, it wins over the saved token, so update or unset it too. Exit code 3.
Auto-delete isn't set up for this destination
--expires needs the bucket's auto-delete rules. Set them up once from Delete after in Aktar's menu bar or the destination's settings, or upload without --expires. Exit code 1.
No destination named "…"
The message lists the names Aktar has. Check the spelling, quote names with spaces (-d "Client work"), or use the ID from aktar destinations. Exit code 2.
A big upload takes a long time
That's the storage provider's speed: aktar has no time limit for uploads and shows progress on stderr in a terminal. Keep Aktar running until it's done. If the connection drops or Aktar quits (Aktar closed the connection before the request finished), upload the same file again: Aktar for Mac 0.10.0 and Windows 0.3.0 continue from the last part.
aktar won't start: SyntaxError or parseArgs
Your Node.js is older than 18. Check with node --version and update it, or install aktar with Homebrew, which brings its own Node.js.
PowerShell says running scripts is disabled
PowerShell's execution policy blocks the script npm installs for aktar. Run aktar.cmd instead, or allow local scripts once with Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.