You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
Matteo Benedetto f5216b1733 feat: add Flatpak packaging and private repo helpers 2 weeks ago
app Initial commit — Cellar MVP 2 weeks ago
flatpak feat: add Flatpak packaging and private repo helpers 2 weeks ago
scripts feat: add Flatpak packaging and private repo helpers 2 weeks ago
.dockerignore Initial commit — Cellar MVP 2 weeks ago
.gitignore feat: add Flatpak packaging and private repo helpers 2 weeks ago
Dockerfile Initial commit — Cellar MVP 2 weeks ago
Dockerfile.build build: add Ubuntu 22.04 portable binary pipeline 2 weeks ago
README.md feat: add Flatpak packaging and private repo helpers 2 weeks ago
cellar feat: add PyInstaller spec for creating portable executable of Cellar GTK 2 weeks ago
cellar-cli.py feat: add CLI client, GTK client rename, logo, config support and compiled binaries 2 weeks ago
cellar-cli.spec feat: add CLI client, GTK client rename, logo, config support and compiled binaries 2 weeks ago
cellar-gtk.py feat: add Flatpak packaging and private repo helpers 2 weeks ago
cellar.jpg feat: add CLI client, GTK client rename, logo, config support and compiled binaries 2 weeks ago
cellar.spec feat: improve portability – fix spec entry point, add hicolor icons, force Adwaita theme on KDE 2 weeks ago
client.py feat: add CLI client, GTK client rename, logo, config support and compiled binaries 2 weeks ago
docker-compose.yml Initial commit — Cellar MVP 2 weeks ago
net.enne2.Cellar.yaml feat: add Flatpak packaging and private repo helpers 2 weeks ago
requirements.txt Initial commit — Cellar MVP 2 weeks ago

README.md

Cellar

Cellar logo

Author: Matteo Benedetto

Cellar is a minimal self-hosted prototype for archiving exported Bottles environments and restoring them on another Linux system.

The name refers to a wine cellar — the place where bottles are stored and preserved.

The project currently provides:

  • a FastAPI server
  • a local SQLite database for archive metadata
  • local file storage for uploaded bottle backups
  • a minimal Python CLI client
  • a small GTK4 desktop client for browsing and installing archived bottles
  • a Docker Compose setup for running the server quickly

The current goal is simple: make it possible to back up a local Bottles bottle, upload it to a Cellar server, list what is stored, and reinstall a bottle by name.


Current status

This is an early MVP.

Implemented features:

  • health endpoint
  • archive upload
  • archive listing
  • archive detail lookup
  • archive download
  • archive deletion
  • local Bottles scan from the client
  • interactive backup + upload wizard from the client
  • install a bottle from the server with a simple command:
    • python client.py install <bottle-name>
  • browse server archives from a GTK4 window and install them with visual feedback

Not implemented yet:

  • authentication
  • permissions / multi-user separation
  • editing metadata already stored in the database
  • archive versioning
  • deduplication
  • tags normalization
  • server-side search/filtering
  • direct Bottles GUI integration

Project structure


Architecture

Server

The server is built with FastAPI and stores metadata in SQLite.

Each uploaded archive creates:

  1. a physical file in storage/
  2. a metadata record in the SQLite database in data/

Stored metadata includes:

  • archive display name
  • bottle name
  • description
  • tags
  • architecture
  • runner
  • Windows version
  • original file name
  • stored file name
  • content type
  • file size
  • SHA256 digest
  • creation time

Client

The client is intentionally minimal and uses the Python standard library.

It can:

  • list archives on the server
  • upload an existing archive file
  • download an archive by id
  • scan local Bottles bottles
  • run a wizard to choose a local bottle, create a backup, and upload it
  • install a remote bottle by name

The GTK desktop client builds on the same install flow, but exposes it through a small Linux GUI:

  • refresh the archive list from the server
  • filter archives locally with a quick search field
  • see archive metadata at a glance
  • show when a bottle is already installed locally
  • download and install a bottle locally with progress feedback
  • open a separate configuration window to change the server URL and local Bottles path

Storage format

The current client creates a .tar.gz backup of the selected bottle directory.

The install flow:

  1. finds the archive by bottle name
  2. downloads it
  3. extracts it safely
  4. places it into the local Bottles directory

Because Bottles backups contain dosdevices symlinks, extraction uses a trusted tar extraction mode while still enforcing a path traversal safety check.


Requirements

Recommended local environment:

  • Docker
  • Docker Compose
  • Python 3.12+
  • Bottles installed locally if you want to use scan-local, wizard-upload, or install

Expected Bottles path used by the client by default:

  • ~/.var/app/com.usebottles.bottles/data/bottles/bottles

This matches the Flatpak Bottles installation layout.


Running the server

From bottle-archive-server run:

docker compose up --build

The API is exposed on:

  • http://127.0.0.1:8080

Interactive API docs are available at:

  • http://127.0.0.1:8080/docs

Flatpak package

The repository now includes a Flatpak manifest for the GTK client:

Flatpak packaging assets are stored in flatpak/:

  • desktop entry
  • AppStream metadata
  • launcher script
  • icon generated from cellar.jpg

Typical local build flow:

  1. install flatpak-builder
  2. install the required runtimes:
    • org.gnome.Platform//47
    • org.gnome.Sdk//47
  3. run:

flatpak-builder --user --install --force-clean flatpak-build net.enne2.Cellar.yaml

Then launch it with:

flatpak run net.enne2.Cellar

To export a private Flatpak repository and a single-file bundle, use:

./scripts/build-flatpak-repo.sh

This creates:

  • flatpak-repo/ — OSTree/Flatpak repository ready to publish
  • dist/net.enne2.Cellar.flatpak — installable Flatpak bundle

Publish on brain.local

If brain.local serves static files, copy the generated repository there, for example:

./scripts/publish-flatpak-repo.sh brain.local /var/www/html/flatpak/cellar

Then clients can add the remote and install the app with:

flatpak remote-add --if-not-exists --user --no-gpg-verify brain-local http://brain.local/flatpak/cellar

flatpak install --user brain-local net.enne2.Cellar


Environment configuration

Current environment variables used by the API container:

  • DATABASE_URL
  • STORAGE_DIR

Current defaults in docker-compose.yml:

  • database: sqlite:////app/data/bottle_archive.db
  • storage: /app/storage

Host directories are mounted as volumes:


API endpoints

GET /health

Simple health check.

GET /archives

Returns the list of archives ordered by creation time descending.

POST /archives

Uploads a new archive file and stores metadata.

Form fields supported:

  • file
  • name
  • bottle_name
  • description
  • tags
  • arch
  • runner
  • windows_version

GET /archives/{archive_id}

Returns one archive metadata entry.

GET /archives/{archive_id}/download

Downloads the archive file.

DELETE /archives/{archive_id}

Deletes the archive file and the corresponding metadata row.


Client commands

All commands below assume you are inside bottle-archive-server.

List remote archives

python client.py list

Upload an existing archive file

python client.py upload /path/to/archive.tar.gz --name "Empire Earth Gold" --bottle-name "Empire-Earth-Gold"

Optional extra metadata:

python client.py upload /path/to/archive.tar.gz --name "Empire Earth Gold" --bottle-name "Empire-Earth-Gold" --description "Backup from desktop" --tags "games,gog" --arch win32 --runner soda-9.0-1 --windows-version win7

Download an archive by id

python client.py download 1 ./downloads/

Scan local Bottles bottles

python client.py scan-local

JSON output:

python client.py scan-local --json

Interactive wizard: choose local bottle, back it up, upload it

python client.py wizard-upload

Install a bottle from the server by name

python client.py install Empire-Earth-Gold

Replace existing local bottle:

python client.py install Empire-Earth-Gold --replace

Use a custom Bottles directory:

python client.py install Empire-Earth-Gold --bottles-dir /custom/path

GTK4 desktop client

Run:

python cellar-gtk.py

The GUI lets you:

  • list archives available on the server
  • filter archives by name, bottle, runner, tags, and description
  • immediately see whether a bottle is already installed on the local system
  • download and install a selected bottle locally
  • enable overwrite of existing local bottles
  • open a separate configuration window to set the server address

The GTK client stores its local settings in:

  • ~/.config/cellar/gtk_client.json

On Linux you need GTK4 bindings available in the system Python. Typical packages are:

  • Fedora: python3-gobject and gtk4
  • Debian/Ubuntu: python3-gi and gir1.2-gtk-4.0

Example workflow

Backup and upload

  1. Start the server:
    • docker compose up --build
  2. Scan local Bottles:
    • python client.py scan-local
  3. Start the wizard:
    • python client.py wizard-upload
  4. Choose one of the local bottles
  5. Confirm archive metadata
  6. The client creates a temporary .tar.gz backup and uploads it

Restore on another machine

  1. Start the same server or connect to a reachable one
  2. Install the bottle by name:
    • python client.py install Empire-Earth-Gold
  3. Check local Bottles:
    • python client.py scan-local

Known limitations

1. No authentication

At the moment, anyone who can reach the API can list, download, upload, and delete archives.

This is acceptable only for a local prototype or a trusted LAN environment.

2. No metadata editing

Once an archive is inserted in the database, there is currently no API or client command to update its metadata.

That means fields like:

  • name
  • description
  • tags
  • runner
  • windows_version

cannot yet be corrected or refined without direct database manipulation or deleting and re-uploading the entry.

3. No versions/history model

Multiple uploads of the same bottle are just separate rows. There is no version chain or “latest version” logic yet.

4. Local storage only

The server stores everything on local disk. There is no S3/MinIO/remote object storage integration yet.

5. Flatpak Bottles path assumption

The default client path targets a Flatpak Bottles installation. Native Bottles layouts may need --bottles-dir.


Next phases

The next development phases should focus on two critical areas.

Phase 1: Authentication system integration

This is the most important missing feature.

The project needs an authentication layer so the server can be used safely beyond a private local test.

Recommended additions:

  • user accounts
  • password hashing
  • token-based authentication
  • per-user archive ownership
  • permission checks on download/delete/edit operations
  • optional admin role

Possible implementation direction:

  • FastAPI auth routes
  • JWT access tokens or session tokens
  • password hashing with a standard library such as passlib/bcrypt
  • users table + relation from archive rows to owners

Minimum target for this phase:

  • POST /auth/register
  • POST /auth/login
  • protected archive routes
  • client support for login and storing a token locally

Phase 2: Database entry editing system

The second required feature is a proper metadata editing workflow.

Right now archive metadata is write-once. That is too limiting.

The project needs a system to edit database entries safely from the API and the client.

Recommended additions:

  • PATCH /archives/{id} endpoint
  • validation rules for editable fields
  • audit fields like updated_at
  • optional edit history
  • client command such as:
    • python client.py edit 12 --name "..." --tags "..."

Editable fields should include at least:

  • name
  • bottle_name
  • description
  • tags
  • arch
  • runner
  • windows_version

This phase is important because real archives often need cleanup after upload.


Short term

  • add authentication
  • add metadata editing
  • add archive search/filter endpoints
  • add better error messages in client install/upload flows

Medium term

  • add versioning for the same bottle
  • add per-user archive namespaces
  • add pagination on GET /archives
  • add import/export manifest support
  • add upload-local <bottle-name> non-interactive command

Long term

  • replace local storage with S3/MinIO
  • add a small web UI
  • add full-text search on archive metadata
  • add deduplication / retention policies
  • integrate directly with Bottles workflows more tightly

Security note

Until authentication is implemented, do not expose this API publicly.

Use it only:

  • on localhost
  • on a trusted home LAN
  • behind an authenticated reverse proxy if temporarily needed

Development notes

The project is currently intentionally simple.

That simplicity is useful because it makes the next steps clear:

  1. secure access
  2. allow metadata editing
  3. improve archive lifecycle management

Those two immediate next phases — authentication and database entry editing — should be considered required before treating this project as more than a local prototype.