MiauRizius 3596998f28
Some checks failed
test-and-lint / test-and-lint (push) Successful in 4m18s
Build and Push Docker Image on Release / build-and-push (release) Failing after 12s
Merge pull request 'tried to fix #15' (#16) from bugfix/15-docker-deployment into main
Reviewed-on: #16
2026-06-10 03:43:29 +02:00
2026-06-10 03:42:31 +02:00
2026-06-10 01:35:36 +02:00
2026-06-07 02:08:49 +02:00
2026-06-10 03:24:31 +02:00
2026-06-10 03:24:31 +02:00
2026-06-10 03:24:31 +02:00
2026-06-10 03:24:31 +02:00
2026-06-10 03:24:31 +02:00
2026-06-08 00:34:54 +02:00
2026-06-03 14:10:02 +02:00
2026-06-09 13:45:03 +02:00
2026-06-10 03:24:31 +02:00
2026-06-10 03:24:31 +02:00
2026-06-03 01:52:56 +02:00
2026-06-10 03:24:31 +02:00

MiauInv

MiauInv is a lightweight inventory, stock, and project allocation management system written in Go. It provides a server-rendered dashboard with HTMX-style page composition, vanilla JavaScript for API interactions, SQLite for persistence, JWT-based sessions, refresh-token rotation, account settings, and optional TOTP-based two-factor authentication and WebAuthn passkey authentication.

The project is designed for self-hosted/private deployments. It is not a full enterprise asset-management platform, but it already covers the main workflows needed for tracking items, locations, stock distribution, and project allocations.

Contents

Features

Inventory and allocation

  • Item management with name, category, description, and total quantity.
  • Location management for physical or logical storage places.
  • Stock mapping between items and locations.
  • Project management for allocating items to projects.
  • Association tracking between projects and item quantities.
  • Dashboard statistics for items, locations, and projects.

Account and authentication

  • User registration, if enabled in the configuration.
  • Password hashing with bcrypt.
  • Signed JWT access tokens.
  • Database-backed refresh tokens with rotation.
  • HTTP-only secure cookies for access and refresh tokens.
  • Account settings page at /profile/settings.
  • Passkey registration, login, removal, and full passkey disable from account settings.
  • Username change with password confirmation.
  • Password change with old-password verification and session refresh.

Two-factor authentication

  • Optional TOTP 2FA using authenticator apps.
  • QR-code based setup from the account settings page.
  • Manual setup key fallback if QR scanning is not available.
  • Two-step login flow for accounts with 2FA enabled.
  • Recovery codes generated when 2FA is enabled.
  • Recovery codes are stored only as hashes.
  • Recovery codes can be downloaded as a text file after generation.
  • Recovery codes can be regenerated from account settings.
  • Recovery-code count warnings in account settings.
  • Recovery codes are one-time use.
  • The setup secret is only stored after the first valid authenticator code.
  • Existing refresh sessions are revoked when 2FA is enabled or disabled.

Passkeys

  • WebAuthn-based passkey registration from account settings.
  • Passkey login from the normal sign-in page.
  • Discoverable passkey login without entering a username first.
  • User-verification required for registration and login.
  • Server-side challenge storage using opaque one-time challenge tokens.
  • Passkey removal with current-password confirmation.
  • Full passkey disable with current-password confirmation.
  • Existing refresh sessions are revoked when passkeys are added, removed, or disabled.

Current Status

MiauInv is an active private project. The current version supports core inventory workflows and account-level security settings. Some areas are intentionally still basic:

  • There is no dedicated admin panel yet.
  • Basic in-memory rate limiting protects login, 2FA, refresh, registration, and sensitive account endpoints.
  • Automated testing is currently limited and will be expanded in future releases.
  • The application currently uses native TLS. If deployed behind a reverse proxy, the proxy must connect to the backend over HTTPS or the backend TLS behavior must be adjusted intentionally.

Technical Stack

Area Technology
Backend Go 1.26
Routing Go standard library net/http
Database SQLite via github.com/glebarez/go-sqlite
Authentication JWT via github.com/golang-jwt/jwt/v5
Password hashing bcrypt via golang.org/x/crypto/bcrypt
2FA TOTP via github.com/pquerna/otp/totp
Passkeys WebAuthn via github.com/go-webauthn/webauthn
Frontend Server-rendered HTML, HTMX-style structure, vanilla JavaScript
Styling Custom CSS with dark theme variables
Deployment Docker / Docker Compose

Architecture

The codebase is split into small packages with mostly direct responsibilities:

Path Responsibility
main.go Application entrypoint. Initializes configuration, database, and server startup.
server/ HTTP route registration, TLS listener, and server-level configuration.
config/ Runtime configuration file creation and loading.
auth/ JWT generation, JWT validation, middleware, role middleware, and password helpers.
handlers/ JSON API handlers for authentication, account settings, inventory, locations, projects, stock, and associations.
storage/ SQLite schema setup, migrations, and database access helpers.
models/ Shared data structures and constants.
frontend/ HTML template rendering and static asset serving.
frontend/assets/js/ Frontend API client, login flow, token refresh logic, account settings logic, and dashboard actions.
frontend/htmx/ HTML views and dashboard content templates.

Documentation

More detailed documentation is available in:

Configuration

MiauInv reads ./appdata/config.yaml. If the file does not exist, the application creates a default configuration on startup.

port: "8080"
database_path: ./appdata/database.db
certificate_path: ./appdata/cert.pem
private_key_path: ./appdata/key.pem
allow_registration: true

Configuration fields

Field Description
port HTTPS listen port.
database_path SQLite database path.
certificate_path TLS certificate path.
private_key_path TLS private key path.
allow_registration Enables or disables public registration. If false, /register renders a blocked-registration page and /api/register is not registered.

Environment variables

Variable Required Description
JWT_SECRET Yes Symmetric signing secret for JWTs. Must be at least 32 characters.

Generate a local development secret with:

openssl rand -base64 48

Routes and API Endpoints

Frontend routes

Route Method Auth required Description
/ GET No Landing page.
/login GET No Login page. Supports password login and the second 2FA step.
/register GET No Registration page or blocked-registration page, depending on configuration.
/dashboard GET Yes Dashboard overview.
/inventory GET Yes Stock and inventory overview.
/items GET Yes Item management view.
/locations GET Yes Location management view.
/projects GET Yes Project management view.
/profile/settings GET Yes Account settings, password changes, passkey management, 2FA setup, 2FA disable, and recovery-code management.
/profile/ GET No Placeholder page for unfinished profile subpages.
/assets/* GET No Static CSS/JS assets. Minified CSS/JS variants are generated on request.

Authentication and account API

Endpoint Method Auth required Description
/api/register POST No Creates a user if registration is enabled.
/api/login POST No Validates username/password. Returns a full session if 2FA is disabled; otherwise returns a short-lived 2FA challenge token.
/api/login/2fa POST No Completes login using the 2FA challenge token plus either a TOTP code or a recovery code.
/api/refresh POST No Rotates a refresh token. Accepts the token from JSON or the refresh_token cookie.
/api/logout POST Yes Revokes refresh tokens for the current user and clears auth cookies.
/api/profile GET Yes Returns current user metadata, 2FA state, and unused recovery-code count.
/api/userinfo GET Yes Same user information handler as /api/profile.
/api/account/username POST Yes Changes the current username after password confirmation.
/api/account/password POST Yes Changes the current password, revokes old refresh tokens, and issues a new session.
/api/2fa/setup POST Yes Creates a pending TOTP secret and returns secret, setup_token, otpauth_url, and a base64 PNG QR code.
/api/2fa/enable POST Yes Enables 2FA after validating the temporary setup token and a TOTP code. Replaces recovery codes and revokes old sessions.
/api/2fa/disable POST Yes Disables 2FA after password and TOTP confirmation. Revokes sessions and clears auth cookies.
/api/2fa/recovery-codes/regenerate POST Yes Invalidates existing recovery codes and returns a new set after password and TOTP confirmation.
/api/passkeys GET Yes Lists passkeys registered for the current user.
/api/passkeys DELETE Yes Removes one passkey after current-password confirmation.
/api/passkeys/register/options POST Yes Creates a server-side passkey registration challenge after current-password confirmation.
/api/passkeys/register/finish POST Yes Verifies and stores a new passkey credential. Revokes old sessions and issues a new current session.
/api/passkeys/login/options POST No Creates a discoverable passkey login challenge.
/api/passkeys/login/finish POST No Verifies passkey login and issues a full session.
/api/passkeys/disable POST Yes Deletes all passkeys after current-password confirmation. Revokes old sessions and issues a new current session.

Inventory API

Endpoint Method Auth required Query parameters Description
/api/item GET Yes id optional Returns all items with aggregate quantities, or one item by ID.
/api/item POST Yes None Creates an item.
/api/item PUT Yes id required Updates item fields.
/api/item DELETE Yes id required Deletes an item if no dependent rows block it.
/api/location GET Yes id, content optional Returns all locations, one location, or location contents with content=true.
/api/location POST Yes None Creates a location.
/api/location PUT Yes id required Renames a location.
/api/location DELETE Yes id required Deletes a location if no dependent rows block it.
/api/project GET Yes id, details optional Returns all projects, one project, or project allocations with details=true.
/api/project POST Yes None Creates a project.
/api/project PUT Yes id required Updates a project.
/api/project DELETE Yes id required Deletes a project if no dependent rows block it.
/api/stock GET Yes id optional Returns stock entries.
/api/stock POST Yes None Creates a stock entry.
/api/stock PUT Yes id required Updates a stock entry.
/api/stock DELETE Yes id required Deletes a stock entry.
/api/association GET Yes id, project_id optional Returns project-item associations.
/api/association POST Yes None Allocates item quantity to a project.
/api/association PUT Yes id required Updates a project allocation.
/api/association DELETE Yes id required Removes a project allocation.

Setup

Prerequisites

  • Go 1.26.
  • SQLite-compatible environment.
  • OpenSSL or another way to generate TLS certificates for local development.
  • A JWT_SECRET with at least 32 characters.

Generate development TLS files

The server uses http.ListenAndServeTLS, so TLS files must exist before startup.

mkdir -p appdata
openssl req -x509 -newkey rsa:4096 \
  -keyout appdata/key.pem \
  -out appdata/cert.pem \
  -days 365 \
  -nodes \
  -subj "/CN=localhost"

Native local run

export JWT_SECRET="replace-this-with-a-random-secret-of-at-least-32-chars"
go mod tidy
go build -o miauinv .
./miauinv

Then open:

https://localhost:8080

Docker Deployment

The repository contains a multi-stage Dockerfile. The final image is based on scratch and contains the compiled binary plus frontend assets.

Example docker-compose.yaml:

services:
  miauinv:
    image: git.miaurizius.de/miaurizius/miauinv:latest
    container_name: MiauInv
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - JWT_SECRET=replace-this-with-a-random-secret-of-at-least-32-chars
    volumes:
      - ./appdata:/appdata

Start the container:

docker compose up -d

View logs:

docker compose logs -f

Reverse Proxy Deployment

MiauInv currently listens with native TLS. If it is deployed behind Caddy or another reverse proxy, the proxy must connect to the backend using HTTPS unless the server code is changed to listen without TLS.

Example Caddy configuration with a self-signed backend certificate:

inv.example.com {
        encode zstd gzip

        reverse_proxy https://miauinv:8080 {
                transport http {
                        tls_insecure_skip_verify
                }
        }

        header {
                X-Content-Type-Options nosniff
                Referrer-Policy strict-origin-when-cross-origin
        }
}

For Docker deployments, place Caddy and MiauInv on the same Docker network and reverse proxy to the service name.

Security Notes

  • JWTs are signed, not encrypted. Normal access and purpose tokens must not contain secrets. The temporary 2FA setup token is a narrow exception because it carries the not-yet-enabled TOTP secret back to the authenticated browser until confirmation.
  • JWT_SECRET must be random and private.
  • Access tokens expire after 15 minutes.
  • Refresh tokens expire after 7 days and are rotated on refresh.
  • Refresh tokens and recovery codes are stored in the database as hashes.
  • Passkey credentials store public-key credential data, not private keys. Private keys remain in the authenticator or platform passkey provider.
  • TOTP secrets are currently stored in the database because the server must validate codes. Protect the database file accordingly.
  • Recovery codes are only shown when generated. Users should download or copy them immediately. The UI warns when few unused codes remain.
  • 2FA disable and recovery-code regeneration require both the current password and a valid TOTP code.
  • Basic in-memory rate limiting is enabled for login, passkey ceremonies, 2FA, refresh, registration, and sensitive account endpoints. Use persistent or distributed rate limiting for multi-instance deployments.
  • Automated testing is currently limited. Authentication, 2FA, recovery codes, rate limiting, account settings, and inventory handlers should be covered before production use.

Screenshots

Dashboard

Inventory

Locations

Projects

Description
MiauInv is a secure, light-weight inventory, stock, and project allocation tracking system written in Go. It utilizes HTMX for a dynamic, reactive single-page experience over traditional server-rendered HTML blocks, backed by an encrypted JWT and dual-token refresh architecture alongside an embedded SQLite instance.
https://miauinv.miaurizius.de
Readme 576 KiB
v1.1.1 Latest
2026-06-10 04:31:55 +02:00
Languages
Go 45.5%
JavaScript 25.2%
HTML 22.5%
CSS 6.6%
Dockerfile 0.2%