# Andromeda ## Quickstart Get up and running with any Andromeda app in minutes. ### Prerequisites Make sure [Rust](https://www.rust-lang.org/tools/install) is installed: ```bash rustc --version ``` ### Clone the Workspace ```bash git clone https://github.com/stevedylandev/andromeda.git cd andromeda ``` ### Build All Apps ```bash cargo build --release ``` ### Run a Specific App Each app is a workspace member. Run any of them with `cargo run -p`: ```bash cargo run -p sipp -- server --port 3000 cargo run -p feeds cargo run -p parcels cargo run -p jotts cargo run -p og cargo run -p shrink ``` Most apps start on `http://localhost:3000` by default. Check each app's page for specific environment variables and configuration. ### Environment Variables Most apps use a `.env` file for configuration. Copy the example file and fill in your values: ```bash cp apps//.env.example .env ``` ### Deployment Every app compiles to a single binary with embedded assets, making deployment straightforward. Each app supports: * **Railway** - one-click deploy buttons on each app's page * **Docker** - `docker compose up -d` * **Binary** - copy the binary to your server and run it directly ## What is Andromeda Andromeda is a Rust workspace of minimal, self-hosted web apps. Each app compiles to a single binary powered by [Axum](https://github.com/tokio-rs/axum), [SQLite](https://github.com/rusqlite/rusqlite), and [Askama](https://github.com/djc/askama) templates. ### Philosophy Every app in Andromeda follows the same principles: * **Single binary** - Each app compiles to one small binary (\~7MB) with all assets embedded at compile time * **Minimal resource usage** - Apps average around \~10MB of RAM * **Self-hosted** - You own your data and run it on your own infrastructure * **Simple deployment** - One binary, one optional `.env` file, no external dependencies * **Consistent stack** - All apps share the same Rust stack so patterns are transferable ### Apps | App | Description | | ------------------------ | ----------------------------------------- | | [Sipp](/apps/sipp) | Code sharing with web UI and TUI | | [Feeds](/apps/feeds) | RSS reader with FreshRSS and OPML support | | [Parcels](/apps/parcels) | Package tracking (USPS) | | [Jotts](/apps/jotts) | Markdown notes | | [OG](/apps/og) | Open Graph tag inspector | | [Shrink](/apps/shrink) | Image compression and resizing | ### Shared Crates | Crate | Description | | ---------------- | ------------------------------------- | | `andromeda-auth` | Session-based password authentication | The `andromeda-auth` crate provides a shared authentication layer used across apps that require login, including session cookies and constant-time password verification. ### License [MIT](https://github.com/stevedylandev/andromeda/blob/main/LICENSE) ## Skills Andromeda apps are scaffolded using [Claude Code](https://claude.ai/code) with a custom skill called `rust-crud`. This page explains how to use it to build your own apps in the same style. ### What is rust-crud? `rust-crud` is a Claude Code skill that scaffolds a complete Rust CRUD web application matching the Andromeda stack: * Axum web server with routing * SQLite database with rusqlite * Askama HTML templates * API key authentication * Embedded static assets via rust-embed * Dockerfile and docker-compose.yml for deployment ### Using the Skill In Claude Code, invoke the skill when you want to create a new app: ``` /rust-crud ``` Describe what you want to build and the skill will generate a complete, working app that follows the same patterns as every other Andromeda app. ### Building Your Own App If you want to add a new app to the workspace manually, follow these steps: #### 1. Create the app directory ```bash mkdir -p apps/my-app/src mkdir -p apps/my-app/templates mkdir -p apps/my-app/static ``` #### 2. Add to workspace Add your app to the root `Cargo.toml` workspace members: ```toml [workspace] members = [ "apps/my-app", # ...existing apps ] ``` #### 3. Set up Cargo.toml ```toml [package] name = "my-app" version = "0.1.0" edition = "2021" [dependencies] axum = "0.8" tokio = { version = "1", features = ["full"] } rusqlite = { version = "0.32", features = ["bundled"] } askama = "0.12" askama_axum = "0.4" rust-embed = "8" tower-http = { version = "0.6", features = ["cors"] } serde = { version = "1", features = ["derive"] } dotenvy = "0.15" ``` #### 4. Follow the pattern Use the [Stack](/diy/stack) page as a reference for how to structure your `main.rs`, `server.rs`, and `db.rs` files. Look at any existing app in `apps/` for a working example. #### 5. Build and run ```bash cargo run -p my-app ``` ## Stack Every app in Andromeda is built on the same Rust stack. This page covers the core dependencies and how they fit together. ### Core Dependencies #### Axum [Axum](https://github.com/tokio-rs/axum) is the web framework powering every app. It provides routing, request extraction, middleware, and response handling. All apps follow a similar pattern: ```rust let app = Router::new() .route("/", get(index)) .route("/api/items", post(create_item)) .with_state(app_state); ``` #### SQLite (rusqlite) [rusqlite](https://github.com/rusqlite/rusqlite) provides the storage layer. Each app creates its own SQLite database file, keeping data local and portable. No external database server needed. #### Askama [Askama](https://github.com/djc/askama) handles HTML templating with compile-time checked templates. Templates live in a `templates/` directory and are type-safe Rust structs: ```rust #[derive(Template)] #[template(path = "index.html")] struct IndexTemplate { items: Vec, } ``` #### rust-embed [rust-embed](https://github.com/pyrossh/rust-embed) embeds static assets (CSS, fonts, images) directly into the binary at compile time. This is what makes each app a single, self-contained binary with no external file dependencies. #### Tokio [Tokio](https://tokio.rs) provides the async runtime that Axum runs on. ### Shared Crate: andromeda-auth The `andromeda-auth` crate provides session-based password authentication used across apps that require login. It handles: * Constant-time password verification * Session cookie management * Secure cookie configuration via `COOKIE_SECURE` env var ### App Pattern Every app follows a consistent structure: ``` app/ ├── src/ │ ├── main.rs # Entry point, env vars, starts server │ ├── server.rs # Axum routes and handlers │ ├── db.rs # SQLite database layer │ └── auth.rs # Authentication (if needed) ├── templates/ # Askama HTML templates ├── static/ # Fonts, favicons, styles ├── Dockerfile └── docker-compose.yml ``` This consistency means once you understand one app, you can navigate any of them. ## Feeds Minimal RSS Feeds ### Overview Feeds is a minimal RSS reader that mimics the original experience of RSS. It's just a list of posts. No categories, no marking a post read or unread, and there is no in-app reading. With this approach you have to read the post on the author's personal website and experience it in its original context. * Single Rust binary with embedded assets * Multiple feed sources: URL params, OPML file, or FreshRSS API * Password-protected admin panel for managing subscriptions * Feeds API with JSON and OPML export * Dark themed UI with Commit Mono font ### Quickstart ```bash git clone https://github.com/stevedylandev/andromeda.git cd andromeda cargo run -p feeds # Server running on http://localhost:3000 ``` #### Environment Variables | Variable | Description | Default | | ------------------- | ----------------------------- | ------- | | `FRESHRSS_URL` | URL of your FreshRSS instance | -- | | `FRESHRSS_USERNAME` | FreshRSS username | -- | | `FRESHRSS_PASSWORD` | FreshRSS password | -- | | `ADMIN_PASSWORD` | Password for the admin panel | -- | | `COOKIE_SECURE` | Enable HTTPS-only cookies | `false` | ### Usage There are several built-in ways to source RSS feeds. #### URL Query Param Once you have the app running you can add the following to the URL to source an RSS feed: ``` ?url=https://bearblog.dev/discover/feed/ ``` You can also add multiple URLs by using commas to separate them: ``` ?urls=https://bearblog.dev/discover/feed/,https://bearblog.stevedylan.dev/feed/ ``` #### OPML File If you save a `feeds.opml` file in the root of the project the app will automatically source it and fetch the posts for the feeds inside. #### FreshRSS API If neither of the above are provided the app will default to using a FreshRSS API instance. Set the following environment variables: ``` FRESHRSS_URL= FRESHRSS_USERNAME= FRESHRSS_PASSWORD= ``` #### Admin Panel Feeds includes a password-protected admin panel at `/admin` for managing your FreshRSS subscriptions. Set the `ADMIN_PASSWORD` environment variable to enable it. From the admin panel you can view your current subscriptions and add new feeds directly to your FreshRSS instance. #### Feeds API The `/feeds` endpoint exports your FreshRSS subscriptions in JSON or OPML format: ``` /feeds?format=json /feeds?format=opml ``` ### Structure ``` feeds/ ├── src/ │ ├── main.rs # Axum server with routing, templates, and static asset serving │ ├── feeds.rs # Feed fetching, OPML parsing, and FreshRSS API integration │ ├── auth.rs # Session-based authentication with constant-time password verification │ └── models.rs # Data structures for feeds and FreshRSS responses ├── templates/ # Askama HTML templates ├── assets/ # Static assets embedded at compile time via rust-embed ├── Dockerfile └── docker-compose.yml ``` ### Deployment #### Railway [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/Ezvmhx?referralCode=JGcIp6) #### Docker ```bash cd apps/feeds cp .env.sample .env # Edit .env with your credentials docker compose up -d ``` #### Binary ```bash cargo build --release -p feeds ``` The resulting binary is self-contained with all assets embedded. Copy it to your server with a configured `.env` file and run it directly. ## Jotts A minimal notes app ### Overview A simple, self-hosted markdown note app built with Rust. * Single \~7MB Rust binary with embedded assets * Password authentication with session cookies * Create, edit, and delete markdown notes * Markdown rendering with strikethrough, tables, and task lists * Dark themed UI with Commit Mono font * SQLite for persistent storage ### Quickstart ```bash git clone https://github.com/stevedylandev/andromeda.git cd andromeda cp apps/jotts/.env.example .env # Edit .env with your password cargo run -p jotts ``` #### Environment Variables | Variable | Description | Default | | ---------------- | --------------------------------- | -------------- | | `JOTTS_PASSWORD` | Password for login authentication | `changeme` | | `JOTTS_DB_PATH` | SQLite database file path | `jotts.sqlite` | | `HOST` | Server bind address | `127.0.0.1` | | `PORT` | Server port | `3000` | | `COOKIE_SECURE` | Enable HTTPS-only cookies | `false` | ### Structure ``` jotts/ ├── src/ │ ├── main.rs # App entrypoint, env vars, starts server │ ├── server.rs # Axum router, HTTP handlers, and templates │ ├── auth.rs # Password verification and session management │ └── db.rs # SQLite database layer (notes, sessions) ├── templates/ # Askama HTML templates │ ├── base.html # Base layout with header and nav │ ├── login.html # Login page │ ├── index.html # Note list │ ├── view.html # Single note display │ ├── new.html # Create note form │ └── edit.html # Edit note form ├── static/ # Favicons, og:image, styles, and webmanifest ├── assets/ # Commit Mono font files ├── Dockerfile └── docker-compose.yml ``` ### Deployment #### Railway [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/DLhUhH?referralCode=JGcIp6) #### Docker ```bash cd apps/jotts cp .env.example .env # Edit .env with your password docker compose up -d ``` This will start Jotts on port `3000` with a persistent volume for the SQLite database. #### Binary ```bash cargo build --release -p jotts ``` The resulting binary is self-contained with all assets embedded. Copy it to your server with a configured `.env` file and run it directly. ## OG A simple web tool for inspecting Open Graph tags on any URL. ### Overview A self-hosted Open Graph tag inspector built with Rust. Enter any URL and instantly see its OG metadata. * Single Rust binary with embedded assets * Inspects title, description, image, and other OG tags * Dark themed UI with Commit Mono font * No database needed -- fully stateless ### Quickstart ```bash git clone https://github.com/stevedylandev/andromeda.git cd andromeda cargo run -p og ``` #### Environment Variables | Variable | Description | Default | | -------- | ----------- | ------- | | `PORT` | Server port | `3000` | ### Structure ``` og/ ├── src/ │ ├── main.rs # Entry point and server startup │ ├── server.rs # Axum routes and request handling │ └── og.rs # Open Graph tag fetching and parsing ├── templates/ # Askama HTML templates │ ├── base.html # Base layout │ ├── index.html # Search form │ └── results.html # OG tag results display ├── static/ # Fonts, favicons, and styles ├── Dockerfile └── docker-compose.yml ``` ### Deployment #### Railway [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/OdXBt_?referralCode=JGcIp6) #### Docker ```bash cd apps/og docker compose up -d ``` #### Binary ```bash cargo build --release -p og ``` The resulting binary is self-contained with all assets embedded. Copy it to your server and run it directly. ## Parcels A minimal package tracking app :::warning This app originally used USPS, but starting April 1st 2026, it has become much harder to obtain/maintain API keys. ::: ### Overview A self-hosted package tracker for USPS. Track your packages without logging into USPS every time. * Single \~7MB Rust binary * Averages around \~10MB of RAM usage * Password authentication * Track USPS packages with custom labels * Delete packages you no longer want to track ### Quickstart ```bash git clone https://github.com/stevedylandev/andromeda.git cd andromeda cp apps/parcels/.env.example .env # Edit .env with your USPS API credentials and app password cargo run -p parcels ``` You'll need a [USPS Web Tools API](https://developer.usps.com) account to get your `USPS_CLIENT_ID` and `USPS_CLIENT_SECRET`. #### Environment Variables | Variable | Description | Default | | -------------------- | --------------------------------- | ------------ | | `APP_PASSWORD` | Password for login authentication | *required* | | `PARCELS_DB_PATH` | SQLite database file path | `parcels.db` | | `USPS_CLIENT_ID` | USPS OAuth2 client ID | *required* | | `USPS_CLIENT_SECRET` | USPS OAuth2 client secret | *required* | | `PORT` | Server port | `3000` | | `COOKIE_SECURE` | Enable HTTPS-only cookies | `false` | ### Structure ``` parcels/ ├── src/ │ ├── main.rs # Axum web server, routes, and app state │ ├── auth.rs # Password verification and session management │ ├── db.rs # SQLite database layer (packages, events, sessions) │ └── usps.rs # USPS API integration with OAuth2 token caching ├── templates/ # Askama HTML templates │ ├── base.html # Base layout │ ├── index.html # Package list │ ├── detail.html # Package detail with tracking events │ ├── add.html # Add package form │ └── login.html # Login page ├── static/ # Fonts, favicons, and images ├── Dockerfile └── docker-compose.yml ``` ### Deployment #### Railway [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/HNQUs4?referralCode=JGcIp6) #### Docker ```bash cd apps/parcels cp .env.example .env # Edit .env with your credentials docker compose up -d ``` This will start Parcels on port `3000` with a persistent volume for the SQLite database. #### Binary ```bash cargo build --release -p parcels ``` The resulting binary is self-contained (\~7MB). Copy it to your server with a configured `.env` file and run it directly. ## Posts A minimal CMS blog with admin interface ### Overview A self-hosted blog CMS built with Rust. * Single Rust binary with embedded assets * Password authentication with session cookies * Create, edit, publish, and delete blog posts with markdown * Static pages with custom navigation links * File uploads with admin management * Custom CSS support from the admin panel * RSS feed at `/feed.xml` * Dark themed UI with Commit Mono font * SQLite for persistent storage ### Quickstart ```bash git clone https://github.com/stevedylandev/andromeda.git cd andromeda cp apps/posts/.env.example .env # Edit .env with your password cargo run -p posts ``` #### Environment Variables | Variable | Description | Default | | ---------------- | --------------------------------- | ----------------------- | | `POSTS_PASSWORD` | Password for admin login | `changeme` | | `POSTS_DB_PATH` | SQLite database file path | `posts.sqlite` | | `UPLOADS_DIR` | Directory for uploaded files | `uploads` | | `SITE_URL` | Public URL for RSS feed and links | `http://localhost:3000` | | `HOST` | Server bind address | `127.0.0.1` | | `PORT` | Server port | `3000` | | `COOKIE_SECURE` | Enable HTTPS-only cookies | `false` | ### Structure ``` posts/ ├── src/ │ ├── main.rs # App entrypoint, env vars, starts server │ ├── server.rs # Axum router, HTTP handlers, and templates │ ├── auth.rs # Password verification and session management │ └── db.rs # SQLite database layer (posts, pages, files, settings, sessions) ├── templates/ # Askama HTML templates │ ├── base.html # Public base layout │ ├── index.html # Blog home page │ ├── post.html # Single post view │ ├── posts.html # Post listing │ ├── page.html # Static page view │ ├── login.html # Login page │ ├── admin_base.html # Admin layout │ ├── admin_index.html # Admin dashboard │ ├── admin_post_form.html # Create/edit post form │ ├── admin_pages.html # Admin page listing │ ├── admin_page_form.html # Create/edit page form │ ├── admin_files.html # File upload management │ └── admin_settings.html # Blog settings ├── static/ # Favicons, fonts, and styles ├── uploads/ # Uploaded files directory ├── Dockerfile └── docker-compose.yml ``` ### Deployment #### Railway [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/tYtJYp?referralCode=JGcIp6) #### Docker ```bash cd apps/posts cp .env.example .env # Edit .env with your password docker compose up -d ``` This will start Posts on port `3000` with a persistent volume for the SQLite database and uploads. #### Binary ```bash cargo build --release -p posts ``` The resulting binary is self-contained with all assets embedded. Copy it to your server with a configured `.env` file and run it directly. ## Shrink A minimal image compression app ### Overview A simple self-hosted tool for compressing and resizing images. Upload an image, set your desired quality and optional width, and download the compressed JPEG. * Single Rust binary * Compress images to JPEG with configurable quality (1-100) * Optional resize by width (preserves aspect ratio) * 20MB upload limit ### Quickstart ```bash git clone https://github.com/stevedylandev/andromeda.git cd andromeda cargo run -p shrink ``` #### Environment Variables | Variable | Description | Default | | -------- | ---------------- | ----------- | | `HOST` | Server bind host | `127.0.0.1` | | `PORT` | Server bind port | `3000` | ### Structure ``` shrink/ ├── src/ │ ├── main.rs # Entry point and server startup │ └── server.rs # Axum routes and image compression logic ├── templates/ │ └── index.html # Upload UI ├── static/ # Fonts and static assets ├── Dockerfile └── docker-compose.yml ``` ### Deployment #### Railway [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/enYUFb?referralCode=JGcIp6) #### Docker ```bash cd apps/shrink docker compose up -d ``` This will start Shrink on port `3000`. #### Binary ```bash cargo build --release -p shrink ``` The resulting binary is self-contained. Copy it to your server and run it directly. ## Sipp Minimal code sharing ### Overview A single binary for code sharing with a web server and interactive TUI. * Create snippets and share on the web * Raw output for CLI tools -- `curl`, `wget`, and `httpie` get plain text automatically * Interactive TUI with authenticated access for snippet management * Minimal, fast, and low memory consumption :::warning A small demo instance runs at [sipp.so](https://sipp.so). All snippets created there are public and might be deleted at any time; host your own instance with your own API key for personal use! ::: ### Quickstart ```bash cargo install sipp-so sipp --help ``` Start a server and create a snippet: ```bash sipp server --port 3000 ``` ```bash # Path to file sipp path/to/file.rs # Or use the interactive TUI sipp ``` #### Install Sipp can be installed several ways: ##### Homebrew ```bash brew install stevedylandev/tap/sipp-so ``` ##### Cargo ```bash cargo install sipp-so ``` ##### Releases Visit the [releases](https://github.com/stevedylandev/sipp/releases) page for binaries and install scripts. ### CLI ``` sipp [OPTIONS] [FILE] [COMMAND] ``` #### Commands | Command | Description | | -------- | ------------------------------------------ | | `server` | Start the web server | | `tui` | Launch the interactive TUI | | `auth` | Save remote URL and API key to config file | #### Arguments | Argument | Description | | -------- | ---------------------------------- | | `[FILE]` | File path to create a snippet from | #### Options | Option | Description | | --------------------- | ---------------------------------------------------------- | | `-r, --remote ` | Remote server URL (env: `SIPP_REMOTE_URL`) | | `-k, --api-key ` | API key for authenticated operations (env: `SIPP_API_KEY`) | ### Server Sipp includes a built-in web server powered by Axum. Start it with: ```bash sipp server --port 3000 --host localhost ``` #### Environment Variables | Variable | Description | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `SIPP_API_KEY` | API key for protecting endpoints | | `SIPP_AUTH_ENDPOINTS` | Comma-separated list of endpoints requiring auth: `api_list`, `api_create`, `api_get`, `api_delete`, `all`, or `none` (defaults to `api_delete,api_list`) | | `SIPP_MAX_CONTENT_SIZE` | Maximum snippet content size in bytes (defaults to `512000` / 500 KB) | | `SIPP_DB_PATH` | Custom path for the SQLite database file (defaults to `sipp.sqlite`) | #### API Endpoints | Method | Endpoint | Description | | -------- | -------------------------- | ------------------------------------------------------ | | `GET` | `/api/snippets` | List all snippets | | `POST` | `/api/snippets` | Create a snippet (`{"name": "...", "content": "..."}`) | | `GET` | `/api/snippets/{short_id}` | Get a snippet by ID | | `PUT` | `/api/snippets/{short_id}` | Update a snippet | | `DELETE` | `/api/snippets/{short_id}` | Delete a snippet by ID | Authenticated endpoints require an `x-api-key` header. #### Raw Output for CLI Tools When you access a snippet URL (`/s/{short_id}`) with `curl`, `wget`, or `httpie`, the server returns the raw content as plain text instead of HTML: ```bash curl https://sipp.so/s/abc123 ``` ### TUI The Sipp TUI makes it easy to create, copy, share, and manage your snippets either locally or remotely. ```bash # Launch TUI (default behavior when no file argument is given) sipp # Or explicitly sipp tui # With remote options sipp -r https://sipp.so -k your-api-key ``` #### Local Access If you are running `sipp` in the same directory as the `sipp.sqlite` file created by the server instance, the TUI will automatically access the database locally. #### Remote Access To access a remote instance: * Set the `SIPP_API_KEY` variable in your server instance * Run `sipp auth` to enter your server URL and API key, stored under `$HOME/.config/sipp` #### Keybindings | Key | Action | | ---------- | ------------------------------ | | `j`/`Down` | Move down / Scroll down | | `k`/`Up` | Move up / Scroll up | | `Enter` | Focus content pane | | `Esc` | Back / Quit | | `y` | Copy snippet content | | `Y` | Copy snippet link | | `o` | Open in browser | | `e` | Edit snippet | | `d` | Delete snippet | | `c` | Create snippet | | `/` | Search snippets | | `r` | Refresh snippets (remote only) | | `q` | Quit | | `?` | Toggle help | ### Structure ``` sipp/ ├── src/ │ ├── main.rs # CLI argument parsing and entry point │ ├── lib.rs # Library exports │ ├── server.rs # Axum web server, routes, and templates │ ├── tui.rs # Interactive terminal UI │ ├── db.rs # SQLite database layer │ ├── backend.rs # Local/remote backend abstraction │ ├── config.rs # Config file management │ └── highlight.rs # Syntax highlighting with custom themes ├── templates/ # Askama HTML templates ├── static/ # Fonts, favicons, and styles ├── Dockerfile └── docker-compose.yml ``` ### Deployment #### Railway [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/Axcf_D?referralCode=JGcIp6) #### Docker ```bash SIPP_API_KEY=your-secret-key docker compose up -d ``` #### Binary ```bash cargo build --release -p sipp ``` The resulting binary is self-contained with all assets embedded. Copy it to your server with your environment variables configured and run it directly.