cloud and shit
  • Rust 71.6%
  • JavaScript 19.4%
  • CSS 5.8%
  • Nix 1.7%
  • HTML 0.6%
  • Other 0.8%
Find a file
2026-10-26 19:09:38 +00:00
crates feat: add family plugin 2026-10-26 19:09:38 +00:00
dev feat: add family plugin 2026-10-26 19:09:38 +00:00
docker feat: add family plugin 2026-10-26 19:09:38 +00:00
docs feat: add family plugin 2026-10-26 19:09:38 +00:00
migrations feat: add family plugin 2026-10-26 19:09:38 +00:00
nix feat: add family plugin 2026-10-26 19:09:38 +00:00
plugins feat: add family plugin 2026-10-26 19:09:38 +00:00
wit feat: add family plugin 2026-10-26 19:09:38 +00:00
.dockerignore feat: add docker compose 2026-10-23 18:15:58 +01:00
.envrc first commit 2026-10-23 15:04:28 +01:00
.gitignore feat: add docker compose 2026-10-23 18:15:58 +01:00
athena.example.toml feat: cookbook plogin 2026-10-26 12:50:23 +00:00
Cargo.lock feat: cookbook plogin 2026-10-26 12:50:23 +00:00
Cargo.toml feat: add nextcloud auth compatability for third party apps 2026-10-23 20:06:22 +01:00
CLAUDE.md feat: add family plugin 2026-10-26 19:09:38 +00:00
compose.yaml first commit 2026-10-23 15:04:28 +01:00
Dockerfile feat: add family plugin 2026-10-26 19:09:38 +00:00
flake.lock first commit 2026-10-23 15:04:28 +01:00
flake.nix feat: add family plugin 2026-10-26 19:09:38 +00:00
README.md feat: add family plugin 2026-10-26 19:09:38 +00:00
rustfmt.toml first commit 2026-10-23 15:04:28 +01:00

Athena

A self-hosted cloud in Rust, in the spirit of Nextcloud: files, calendars, contacts, email, sharing with internal and external people, Collabora Online, WASM plugins and an MCP server for AI assistants — on PostgreSQL, with any storage backend OpenDAL supports.

Athena is client-agnostic: everything is reachable through standard protocols (WebDAV, CalDAV, CardDAV) and a JSON REST API. The built-in web client is just another API consumer and can be compiled out.

Features

Area What works today
Accounts Users, groups, admins, quotas, disabled accounts, Argon2id passwords, browser sessions, per-device app passwords with optional scopes (dav, mcp)
Files WebDAV class 1/2 (PROPFIND/PROPPATCH/GET/HEAD/PUT/MKCOL/DELETE/COPY/MOVE/LOCK/UNLOCK), Range downloads, If-Match/If-None-Match, trash with retention, version history, quota enforcement, etag/size propagation for sync clients, ownCloud/Nextcloud oc: properties, /remote.php/dav and /remote.php/webdav aliases
Nextcloud clients Nextcloud's desktop and mobile apps, DAVx⁵'s Nextcloud login and third-party apps that "log in with Nextcloud": status.php, Login Flow v2 and v1 (log in in the browser, the app receives its own app password), OCS capabilities, current user and app password exchange/revoke, the OCS sharing API behind the apps' share dialogs, chunked uploads (/remote.php/dav/uploads, Nextcloud chunking v1 and v2), OC-FileId/OC-ETag/X-OC-MTime headers; plugins can serve a Nextcloud app's API at /index.php/apps/<id>/. No PHP involved: these are just the URLs Nextcloud clients call
Storage Metadata in Postgres, content in OpenDAL (fs, s3, memory; more backends are a cargo feature away); immutable blobs so moves never touch storage; background blob GC
Sharing Internal shares to users and groups (mounted under /Shared), external public links and email links; per-share permissions (read / edit / upload / delete / reshare), passwords, expiry, download limits, hide-download, upload-only "file drop"; reshares are bounded by their parent share; instance policy (links on/off, password required, default/max expiry, public upload, group restrictions, excluded groups)
CalDAV Principal discovery, .well-known, calendar homes, MKCALENDAR / extended MKCOL, PROPPATCH (name, colour, order, timezone), calendar-multiget, calendar-query (component + time-range), sync-collection, no-uid-conflict / valid-calendar-data, read-only and read-write calendar sharing with users and groups
Calendar app The calendar plugin: day/week/month/agenda views, drag to create/move/resize, recurring events (edit one occurrence, following, or all), time zones, reminders, calendar management, sharing, .ics import/export, a read-only Contact birthdays calendar kept in sync with the contacts' birthdays and anniversaries (also over CalDAV), and MCP tools; all on the user's CalDAV calendars
CardDAV Address book homes, extended MKCOL, addressbook-multiget, addressbook-query, sync-collection
Contacts app The contacts plugin: address books and groups, search (including phone numbers by digits), editing with labels, photos and birthdays, .vcf import/export, finding and merging duplicates (same name and a phone number or email in common, also while importing), and MCP tools to add, edit and remove contacts; all on the user's CardDAV address books, keeping data written by other clients (Apple labels, X- properties)
Paste app The paste plugin: links to pasted text, an uploaded file or a file from your files that expire after a time and/or a number of views (burn after reading), with previews for visitors; upload links anyone can send you files or text through, with limits on time, uploads and file size; received items saved to your files in one click; MCP tools
Cookbook app The cookbook plugin: recipes with photos (resized in the browser), imported from recipe websites (their schema.org data, with the photo), from pasted text or Markdown, from Nextcloud Cookbook folders and recipe files in your files, or with a bookmarklet for sites that block servers; scaling to any number of servings (notes like "(2 sticks; 225 g)" scale along), metric/US conversion (cups of flour to grams through the ingredient's density, °F/°C, pan sizes, US or metric cup), a kitchen converter, cook mode (one step at a time, timers from the steps, the screen kept on), favourites, ratings and a cooked history, a weekly meal plan that can appear as a "Meal plan" calendar on your phone, a shopping list that merges ingredients across recipes and sorts them by aisle, public links (logged-in visitors can save a copy), export to Markdown or schema.org JSON, home screen widgets, and MCP tools
Family app The family plugin: a map of where the people in your family groups are, shown with their profile pictures; groups people join by invitation, each member choosing whether to share with a group or pausing sharing altogether; places (geofences with an icon and radius, found by address search) that announce arrivals and departures in the group's activity and as notifications; positions from the browser while Athena is open, or in the background from phones through the OwnTracks app (set up by QR code; it shows the others too); today's path, map tiles cached on the server, a home screen widget, and MCP tools (“where is everyone?”)
Mail app The mail plugin: connect Gmail, Outlook/Microsoft 365 and any IMAP/SMTP account (server settings found from the address; app passwords, or Sign in with Google/Microsoft when the admin sets up OAuth), a unified inbox across accounts or one account at a time, folders (create, rename, delete, drag messages onto them), conversations, search with operators (from:, is:unread, has:attachment, tag:, in:, dates, server-side body:), tags stored as IMAP keywords (Thunderbird-compatible), star/read/archive/delete, a rich-text composer with recipient suggestions from contacts and mail history, attachments from your computer or your files, drafts, undo send, an outbox that retries, HTML mail in a sandboxed frame with remote images blocked until allowed, attachments saved to your files, new mail pushed by IMAP IDLE, an unread-mail widget, and MCP tools to search, read, tag, file, draft and send
Office Collabora Online via WOPI: new blank documents (text, spreadsheet, presentation, drawing; ODF or OOXML), discovery, CheckFileInfo / GetFile / PutFile, LOCK / UNLOCK / REFRESH_LOCK / GET_LOCK, proof-key signature validation (when Collabora publishes a key), conflict detection (X-COOL-WOPI-Timestamp), editing from public links (with guest names and hide-download → no export/print)
Plugins WebAssembly components (WIT interface in wit/); event subscriptions, a synchronous check-write veto hook, HTTP routes under /apps/<id>/ with an optional entry in the web client's navigation, MCP tools, sandboxed KV storage (with compare-and-swap), blob storage that browsers upload to and download from directly (Range support, zero-copy import from and export to the user's files), a once-a-minute timer event, file access, the user's calendars, contacts and email accounts, and fetching pages from the public web, all gated by manifest permissions; per-call memory limit and time budget
MCP Streamable-HTTP MCP server at /mcp with 16 built-in tools for files, shares, calendars and contacts, plus tools from plugins (e.g. calendar_*, contacts_*, mail_*, paste_*, cookbook_*, family_*), all running with the caller's permissions
Web client Leptos (Rust → WASM) SPA embedded in the binary: a home screen of widgets each user arranges (recent files, storage, shared with me, folders, notes, bookmarks, plus widgets from plugins such as upcoming events and birthdays), files (drag-and-drop upload, new folders, text/Markdown files and office documents, rename, delete, share dialog, open in office), a file viewer (text editor with conflict detection, Markdown editor with live preview, images with previous/next, video, audio, PDF; also on public links), shared-with-me, my shares, trash, settings (appearance, app passwords, connection URLs), admin (users, groups, plugins), public share pages with password unlock and uploads, and plugin apps (such as the calendar) in the sidebar
Personalisation A profile picture (cropped in the browser, shown to the people you work with: on the Family map, in the top bar, and to Nextcloud clients; initials otherwise); per-user theme (light/dark/system, colour presets, accent and custom colours per mode, corner radius, text size, font) applied to the web client and plugin pages alike through /api/v1/me/theme.css; per-user home screen layout

Architecture

crates/
  athena-types    wire types shared by server and web client (compiles to wasm)
  athena-core     config, DB, auth, VFS, permission-checked FsView, sharing, CalDAV/CardDAV storage, events, hooks
  athena-dav      WebDAV + CalDAV + CardDAV protocol layer
  athena-wopi     Collabora Online (WOPI host)            — feature `collabora`
  athena-plugins  wasmtime component host                 — feature `plugins`
  athena-mcp      MCP server (rmcp, streamable HTTP)       — feature `mcp`
  athena-mail     users' email accounts: IMAP sync, SMTP outbox, OAuth — feature `mail`
  athena-server   the `athena` binary: CLI, REST API, router, embedded web client — feature `web-client`
  athena-web      Leptos web client (built with trunk)
migrations/       Postgres schema (applied automatically)
wit/              plugin interface
plugins/          calendar, contacts, mail, paste, cookbook and family apps, and an example plugin (audit-log)

Every frontend (WebDAV, REST, WOPI, MCP, plugins) goes through the same permission-checked FsView in athena-core, so share permissions, locks, quotas and plugin hooks apply identically everywhere. See docs/ARCHITECTURE.md.

Quick start

nix develop                     # Rust + wasm targets, trunk, wasm-tools, psql
docker compose up -d db         # Postgres on localhost:5433

# Build the web client (skip this for a headless server)
(cd crates/athena-web && trunk build --release)

cp athena.example.toml athena.toml   # edit database.url, public_url, storage
cargo run --release -- -c athena.toml migrate
ATHENA_PASSWORD='change-me-please' cargo run --release -- -c athena.toml user add admin --admin
cargo run --release -- -c athena.toml serve

Open http://localhost:8080. For development there is a ready config in dev/athena.toml.

Compile-time features

athena-server features (all on by default):

Feature Adds
web-client the embedded Leptos client (build crates/athena-web/dist first)
collabora WOPI host + office endpoints
plugins the wasmtime plugin host
mcp the /mcp endpoint
mail email accounts for plugins with the mail permission (implies plugins)

A headless, protocol-only server:

cargo build --release -p athena-server --no-default-features

Docker

docker/ runs Athena and PostgreSQL with Docker Compose, plus Collabora Online when you turn it on. The image is built from this checkout: the server, the web client and the plugins compile inside Docker, so you need nothing but a recent Docker (Engine 26 or newer, Compose v2). The first build takes a while.

cd docker
cp .env.example .env            # set POSTGRES_PASSWORD and ADMIN_PASSWORD
docker compose up -d            # builds the image, then starts it

Open http://localhost:8080 and sign in as admin with ADMIN_PASSWORD.

All settings live in docker/.env:

Variable
POSTGRES_PASSWORD database password (required; letters and digits, e.g. openssl rand -hex 24)
ATHENA_SERVER__PUBLIC_URL the URL people open Athena at (default http://localhost:8080)
HTTP_PORT host port Athena is published on; 127.0.0.1:8080 to listen on localhost only
ADMIN_USERNAME, ADMIN_PASSWORD administrator created on first start (the password is not used afterwards)
BUNDLED_PLUGINS plugins to install: calendar contacts mail paste cookbook family by default, audit-log is also in the image
ATHENA_<SECTION>__<KEY> any other option from athena.example.toml, e.g. ATHENA_FILES__DEFAULT_QUOTA_BYTES

Athena keeps files and plugins in the data volume and its metadata in the db volume; back up both together. Day-to-day commands, run in docker/:

docker compose logs -f athena
docker compose exec athena athena user list
docker compose exec -e ATHENA_PASSWORD='…' athena athena user add alice --email alice@example.com
docker compose exec athena athena group add staff
docker compose exec db pg_dump -U athena athena > athena.sql
git pull && docker compose up -d   # update: rebuilds, migrations run on start

To add a plugin of your own, copy its folder (plugin.toml and the .wasm) with docker compose cp myplugin athena:/var/lib/athena/plugins/ and rescan under Admin → Plugins.

Office editing. Uncomment COMPOSE_FILE=compose.yaml:compose.office.yaml in .env and run docker compose up -d again. That adds Collabora Online (CODE) on http://localhost:9980, turns on [collabora] in Athena, connects the two over the compose network and gives Collabora a WOPI proof key, so New → Document and Open in office work right away. Comment the line out and run docker compose up -d --remove-orphans to turn it off again.

Behind a reverse proxy. For a public server, terminate TLS in a proxy on the same machine and publish the containers on localhost only:

# docker/.env
ATHENA_SERVER__PUBLIC_URL=https://cloud.example.com
HTTP_PORT=127.0.0.1:8080
COLLABORA_URL=https://office.example.com
COLLABORA_SERVER_NAME=office.example.com
COLLABORA_SSL_TERMINATION=true
COLLABORA_PORT=127.0.0.1:9980
cloud.example.com {
	reverse_proxy 127.0.0.1:8080
}
office.example.com {
	reverse_proxy 127.0.0.1:9980
}

With nginx, proxy both hosts the same way, allow WebSockets for Collabora, and for Athena set client_max_body_size 0, proxy_request_buffering off and proxy_buffering off so uploads, downloads and MCP streams are not buffered (see nix/module.nix for a complete example).

NixOS

The flake has a package (web client included), the plugins, and a NixOS module that exposes every config option and can run Collabora Online next to Athena:

imports = [ athena.nixosModules.default ];
services.athena = {
  enable = true;
  hostName = "cloud.example.com";
  https = true;
  nginx.enable = true;
  admin = { username = "admin"; passwordFile = "/run/secrets/athena-admin"; };
  collabora = { enable = true; hostName = "office.example.com"; };
  settings.sharing.link_password_required = true;   # any athena.toml key
};

See docs/NIXOS.md for secrets, storage, plugins and the Collabora wiring.

Connecting clients

Create an app password in Settings (or POST /api/v1/me/app-passwords) for each device.

Client URL
Nextcloud desktop, Android and iOS apps, and apps with a Nextcloud login https://cloud.example.com (you log in in the browser; the app gets its own app password)
WebDAV (Finder, Dolphin, Nautilus, rclone, Windows) https://cloud.example.com/dav/files/<user>/ (rclone: vendor = nextcloud with /remote.php/dav/files/<user> for chunked uploads)
CalDAV / CardDAV (DAVx⁵, Thunderbird, Apple Calendar/Contacts, GNOME) https://cloud.example.com/ (auto-discovery via .well-known) or https://cloud.example.com/dav/
REST API https://cloud.example.com/api/v1/
MCP https://cloud.example.com/mcp with Authorization: Bearer <app password>

Example MCP client configuration (Claude Code):

claude mcp add --transport http athena https://cloud.example.com/mcp \
  --header "Authorization: Bearer ath_…"

Limit the app password to the mcp scope so it cannot be used for WebDAV. Admins can make MCP read-only with [mcp] allow_writes = false.

Sharing model

Permission bits (Nextcloud-compatible values): 1 read, 2 update, 4 create, 8 delete, 16 reshare.

  • Internal shares (user, group) appear for recipients under /Shared/<name>; recipients can rename or leave them.
  • External shares (link, email) are reachable at /s/<token> (web), /api/v1/public/<token>/… (REST) and /dav/public/<token>/ (WebDAV; the share password is the Basic-auth password).
  • A share can never grant more than the sharer has; files cannot get create/delete; reshares are capped by the share they came from, and lowering a share's permissions cascades to its reshares.
  • Instance-wide rules live in [sharing] (see athena.example.toml).

Collabora Online

With the Docker setup, compose.office.yaml does all of this. For development, with Athena running on the host:

docker compose --profile office up -d
[server]
bind = "0.0.0.0:8080"            # the container must reach Athena

[collabora]
enabled = true
url = "http://localhost:9980"    # as browsers see Collabora
wopi_url = "http://host.docker.internal:8080"  # as Collabora sees Athena

Collabora's aliasgroup1 must list the wopi_url origin.

New → Document / Spreadsheet / Presentation / Drawing in the file browser creates a blank file and opens it in the editor. New files are ODF (odt/ods/odp) unless [collabora] new_file_format = "ooxml" (docx/xlsx/pptx); typing the other extension in the name works either way. The blank templates live in crates/athena-wopi/templates/ and are regenerated by LibreOffice with templates/generate.py.

WOPI requests are authenticated by a per-file access token, and additionally by proof-key signatures when Collabora publishes a proof key (the default CODE image does not). Provide one by mounting an RSA key at /etc/coolwsd/proof_key (openssl genrsa -out proof_key 2048); Athena logs a warning when signatures cannot be checked, and GET /api/v1/office/capabilities reports proof_validation.

Email

The mail app connects users' own mailboxes. Athena keeps each account's folders and message headers in sync in the background (with IMAP IDLE for new mail), downloads a message when it is read, and sends through the account's SMTP server. Passwords and OAuth tokens are stored encrypted with a key derived from the instance secret.

  • Any IMAP/SMTP provider: users type their address and password; the server settings are looked up (well-known providers, Thunderbird's ISP database, the domain's autoconfig file and MX records) and can be edited.
  • Gmail, Yahoo, iCloud, Fastmail… with an app password, or Gmail with Sign in with Google.
  • Outlook.com / Microsoft 365 need Sign in with Microsoft (Microsoft turned off passwords for mail apps).

OAuth needs a client registered by the admin, with the redirect URI <public_url>/apps/mail/oauth/callback:

[mail.google]        # Google Cloud console → APIs & Services → Credentials →
client_id = "…"      # OAuth client ID (Web application); scope https://mail.google.com/
client_secret = "…"

[mail.microsoft]     # Entra admin center → App registrations; delegated
client_id = "…"      # permissions IMAP.AccessAsUser.All, SMTP.Send, offline_access
client_secret = "…"
tenant = "common"

By default Athena refuses mail servers on loopback or private addresses, so users cannot make it probe its own network; allow them with [mail] allow_private_hosts = true (e.g. for a mail server on your LAN). See [mail] in athena.example.toml for the sync interval, IDLE and size limits.

Plugins

Plugins are WebAssembly components built against wit/athena-plugin.wit. See docs/PLUGINS.md, the calendar, contacts, mail, paste, cookbook and family apps, and the smaller example in plugins/audit-log.

cd plugins/calendar && cargo build --release --target wasm32-wasip2
mkdir -p /var/lib/athena/plugins/calendar
cp plugin.toml target/wasm32-wasip2/release/calendar.wasm /var/lib/athena/plugins/calendar/

Then enable it under Admin → Plugins (or set [plugins] auto_enable = true).

Plugins with the web permission (the cookbook's recipe import, the family app's map tiles and address search) only reach the public internet: hosts on loopback and private networks are refused, also after redirects, unless [plugins] allow_private_hosts = true.

REST API (summary)

Endpoint
GET /api/v1/status server info and enabled features
POST /auth/login, POST /auth/logout, GET /me, PUT /me/password session
GET/POST /me/app-passwords, DELETE /me/app-passwords/{id} device credentials
GET /me/preferences, PUT /me/preferences/appearance, PUT/DELETE /me/preferences/home, GET /me/theme.css theme and home screen
PUT/DELETE /me/avatar, GET /users/{id or name}/avatar profile picture (PNG, JPEG, WebP or GIF up to 2 MiB and 4096 px; generated initials without one; ?v=<UserInfo.avatar> may be cached for good)
GET /files?path=, DELETE /files?path=, GET/PUT /files/content?path= listing, delete, download (&inline=true to display it in the browser), upload (If-Match: "<etag>" to only replace that version, If-None-Match: * to only create)
POST /files/mkdir, POST /files/move, POST /files/copy, GET /files/by-id/{id}, GET /files/recent?limit=
GET /files/versions?path=, POST /files/versions/{id}/restore?path= versions
GET/DELETE /trash, POST /trash/{id}/restore, DELETE /trash/{id} trash
GET/POST /shares, PATCH/DELETE /shares/{id}, GET /shares/incoming, PATCH /shares/incoming/{id}, GET /sharees?q= sharing
GET /public/{token}, POST /public/{token}/auth, GET /public/{token}/files, GET/PUT /public/{token}/content external access
POST /office/open, POST /public/{token}/office, GET /office/capabilities Collabora sessions
POST /office/new ({"path": "/Notes.odt"}) blank document; the template is picked by the extension
GET /apps, GET /widgets plugin pages and home screen widgets (with the plugin host)
/admin/users, /admin/groups, /admin/plugins administration

All paths are relative to /api/v1. Authenticate with the session cookie, HTTP Basic (app password) or Authorization: Bearer <app password>.

Status and roadmap

This is a young codebase. Verified end to end against a live server: WebDAV operations, sharing and permission enforcement, public links, CalDAV/CardDAV (including sync), plugins, MCP, the web client, editing + saving a document in Collabora CODE, and the mail app against Dovecot and GreenMail (sync, CONDSTORE, IDLE, sending, drafts, folders, tags, search). Automated tests currently cover parsers and helpers only; an integration test suite is the most important next step.

Known gaps:

  • Nextcloud compatibility covers the client protocol (login flows, WebDAV, chunked uploads, OCS user, capabilities and sharing), not Nextcloud's server apps: a client of Notes, Deck, News, Talk and the like needs a plugin that implements that app's API under /index.php/apps/<id>/. Nextcloud clients get no previews, favourites, notifications, activity, file locking or end-to-end encryption, and checksums are SHA-256 only.
  • The server indexes recurring events conservatively (open-ended rules match every later time range) instead of expanding them, so CalDAV clients do the expansion; the calendar plugin and its calendar_* MCP tools expand occurrences, the built-in list_events tool returns the rule.
  • Calendar sharing is managed through the calendar plugin (no CalDAV sharing extension or invitations/iTIP yet); address books cannot be shared.
  • Email shares do not send notification mail (there is no instance-wide SMTP sender; the mail app sends from users' own accounts).
  • Mail: "Sign in with Google/Microsoft" follows the providers' documented flows but has not been tried against them live yet; Gmail labels show up as folders and tags are IMAP keywords (Gmail's web UI does not show them); no filters/rules (Sieve), S/MIME or PGP, and messages cannot be moved between accounts.
  • Cookbook: sites that refuse requests from servers (such as Chefkoch) can only be imported with the bookmarklet, and then without their photo; steps are converted but not scaled ("half the flour" stays as written); meal plans and shopping lists are per user, not shared.
  • Family: there is no Athena phone app yet; phones share their position in the background through OwnTracks, browsers only while Athena is open in them. Notifications only show while the app (or its widget) is open, or in OwnTracks.
  • WOPI "Save As" (PUT_RELATIVE) and rename are not implemented.
  • No previews/thumbnails, full-text search, federated (OCM) sharing, 2FA, LDAP/OIDC, or OAuth for MCP clients.

License

AGPL-3.0-or-later.