Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ name: CI
on:
workflow_call:
pull_request:
push:
branches:
- main

permissions:
contents: read
Expand Down
24 changes: 4 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,13 @@
# Aperture

## Overview

Aperture creates Jellyfin or Emby accounts from controlled invite links and applies non-admin policy templates.

## Quick start

```bash
cp .env.example .env
docker compose up -d --build
```

Open `http://localhost:8099`, complete browser setup, then sign in with a media-server administrator account.

Complete setup on a trusted network before exposing Aperture publicly. Fresh installs keep state in `./config`;
existing installs should retain their current data path. See [setup and backups](docs/setup.md).

## Documentation

- [Setup](docs/setup.md)
- [Configuration](docs/configuration.md)
- [Unraid](docs/unraid.md)
- [Security](docs/SECURITY.md)
- [Development](docs/development.md)
- [Contributing](docs/CONTRIBUTING.md)
- Get started: [Setup](docs/setup.md) and [Configuration](docs/configuration.md)
- Run Aperture: [Operations](docs/development/operations.md) and [Unraid](docs/unraid.md)
- See what it does: [Capabilities](docs/capabilities.md)
- Contribute: [Development](docs/development/README.md), [Contributing](docs/CONTRIBUTING.md), and [Security](docs/SECURITY.md)
- [License](LICENSE)
- [Issues](https://github.com/mayvqt/Aperture/issues)

Expand Down
2 changes: 1 addition & 1 deletion docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ Keep changes small and limited to secure invitation, registration, policy applic
- Test behavior, security, persistence, and failure paths.
- Version and test schema changes against existing databases.
- Update affected deployment and configuration files.
- Run every check in [development.md](development.md).
- Use the focused checks and final gate in [Validation](development/validation.md).
- Never commit `.env`, databases, credentials, tokens, logs, or generated artifacts.
16 changes: 16 additions & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Capabilities

Aperture provides:

- browser-based first-run configuration for Jellyfin or Emby;
- administrator authentication through the configured media server;
- reusable non-administrator policy templates, including import from an existing user;
- bounded, expiring invite links with usage limits and optional account expiry;
- account creation, policy application, retry, disable, and recovery workflows;
- managed-user and registration history views;
- Discord and generic JSON webhooks with selected events; and
- administrative audit history with bounded retention.

The media server remains the source of truth for users, passwords, and effective
permissions. Aperture stores the state needed to coordinate invitations and
recovery. Configuration details are in [Configuration](configuration.md).
31 changes: 5 additions & 26 deletions docs/development.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,12 @@
# Development

```bash
go run ./cmd/aperture serve
```
Use the [development task index](development/README.md) to find ownership,
architecture, data, validation, operations, and documentation guidance.

Open `http://localhost:8099/setup`. Go does not load `.env` automatically.
For a local server:

```bash
gofmt -w <touched-go-files>
go test ./...
go vet ./...
go test -race ./...
staticcheck ./...
govulncheck ./...
go run ./cmd/aperture serve
```

Keep SQL in `internal/db`, HTTP workflows in `internal/httpserver`, media protocol code in `internal/mediaserver`, and
secret primitives in `internal/security`. Schema changes require a revision update and upgrade/fresh-install tests.

Tests use temporary SQLite databases and fake media-server clients. No live
Jellyfin/Emby accounts or external database containers are needed. Use existing
installed tools; ask before installing missing tools or downloading dependencies.
CI pins tool versions and action commits, and image publishing reuses its full
checks. Run `sh scripts/test-entrypoint.sh` for entrypoint changes and
`CGO_ENABLED=0 go build -trimpath -o ./build/aperture ./cmd/aperture` for release
build validation. Published images report their tag or commit through
`aperture version`; local builds report `dev`.

No browser wrapper is checked in. Use available shared browser tooling with a
disposable config directory and synthetic users/invites; check desktop/mobile,
validation errors, permission denial, and the setup/login/invite flows. Templates,
CSS and JavaScript are embedded, so rebuild before checking rendered changes.
Open `http://localhost:8099/setup`. Go does not load `.env` automatically.
14 changes: 14 additions & 0 deletions docs/development/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Development task index

Read only the pages needed for the task:

- Find ownership or an entry point: [Codebase map](codebase-map.md).
- Change boundaries, request flow, or integrations: [Architecture](architecture.md).
- Change SQLite, migrations, retention, or secrets: [Data](data.md).
- Select checks or gather evidence: [Validation](validation.md).
- Deploy, back up, restore, release, or roll back: [Operations](operations.md).
- Add or update documentation: [Documentation conventions](documentation-conventions.md).

User and operator pages are indexed from the repository [README](../../README.md).
Security-sensitive changes also require [Security](../SECURITY.md). If a task has
no suitable page, create one focused page and link it here.
37 changes: 37 additions & 0 deletions docs/development/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Architecture

`cmd/aperture` loads configuration, opens the store, initializes schema and
runtime secrets, selects a media-server adapter, starts the maintenance worker,
and serves `internal/httpserver`. Handlers depend on narrow store and media-server
interfaces; persistence stays in `internal/db`, while outbound Jellyfin/Emby
protocol details stay in `internal/mediaserver`.

## Sources of truth and contracts

- Jellyfin or Emby owns user identity, passwords, administrator status, and
effective user policy.
- SQLite owns Aperture settings, templates, invite state, registration recovery,
managed-user tracking, sessions, webhooks, and audit records.
- Environment variables and flags override browser-managed settings. The database
owns settings that are not deployment-managed.
- Public HTTP routes are registered in `internal/httpserver/routes.go`.
`/i/{token}` and registration are unauthenticated; `/setup` is available only
before setup completes; `/admin/*` requires a verified media-server admin.
- Jellyfin and Emby request shapes and authorization are provider contracts.
Verify changes against the supported upstream documentation and cover them
with deterministic HTTP fixtures before any opt-in live smoke test.

## Security and concurrency boundaries

Invite tokens, session material, API keys, access tokens, webhook URLs, cookies,
and the entire state directory are sensitive. Preserve CSRF checks, bounded
request bodies, rate limits, trusted-proxy parsing, security headers, redaction,
and no-redirect outbound clients.

SQLite is deliberately limited to one connection and uses WAL, foreign keys, and
a busy timeout. Setup is serialized in-process. Registration reserves invite
capacity before provisioning; ambiguous external failures retain evidence rather
than silently releasing capacity. The maintenance worker reconciles stale work,
retries templates, disables expired users, and prunes audit events. Webhook
deliveries are tracked and receive a bounded graceful-shutdown window. Changes to
these flows must preserve idempotency, bounded work, and cancellation behavior.
20 changes: 20 additions & 0 deletions docs/development/codebase-map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Codebase map

| Path | Ownership |
| --- | --- |
| `cmd/aperture` | CLI dispatch, configuration startup, logging, process lifecycle, and version output. |
| `internal/config` | Environment/flag parsing, defaults, URL validation, and generated encryption-key bootstrap. |
| `internal/db` | SQLite schema, migrations, settings, invites, templates, sessions, registrations, managed users, and audit records. |
| `internal/httpserver` | Routes, middleware, browser workflows, embedded templates/assets, webhooks, and maintenance coordination. |
| `internal/mediaserver` | Provider-neutral contracts and URL rules. |
| `internal/mediaserver/jellyfin`, `emby`, `protocol`, `router` | Provider adapters, HTTP protocol, and runtime provider selection. |
| `internal/security` | Encryption, token helpers, and diagnostic redaction. |
| `scripts`, `docker-entrypoint.sh`, `Dockerfile`, `docker-compose.yml` | Container build, startup, ownership, and regression checks. |
| `templates/unraid` | Unraid application template. |
| `.github/workflows` | CI, image publication, dependency updates, and release announcements. |

The nearest tests live beside their packages. Presentation sources are
`internal/httpserver/templates` and `internal/httpserver/assets`; both are
embedded into the binary.

Update this map when a top-level domain is added or ownership moves.
33 changes: 33 additions & 0 deletions docs/development/data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Data and schema

`internal/db/schema.go` is the schema and revision source of truth.
`PRAGMA user_version` records the revision. The database contains settings,
templates, invites, registrations, audit events, sessions, webhooks, and managed
users. Secret settings, retained invite tokens, access tokens, and webhook URLs
are encrypted with the installation encryption key; hashed identifiers are used
where raw tokens are unnecessary.

`internal/db/store.go` owns connection policy: one connection, WAL mode, foreign
keys, a five-second busy timeout, private directories, and mode `0600` database
files. Keep SQLite operations in `internal/db`; handlers must not become a second
schema or query owner.

## Schema changes

Migrations are forward-only and transactional. Add a new revision instead of
rewriting the meaning of an applied revision. Prefer expand/contract evolution:
add compatible columns or tables, deploy readers/writers that tolerate the
transition, and remove obsolete data only in a later explicitly supported step.
Startup applies every outstanding supported revision in order and rejects
databases newer than the binary.

Every schema change requires:

- a fresh-database test;
- an upgrade test starting at every affected supported revision;
- preservation tests for existing rows and encrypted values;
- failure/rollback-boundary coverage; and
- corresponding backup and release guidance when operator action is required.

Never repair an upgrade by deleting the database. Back up and restore the whole
state set as described in [Operations](operations.md).
28 changes: 28 additions & 0 deletions docs/development/documentation-conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Documentation conventions

Write for one audience and one task per page. Keep commands executable, link to
the source of truth instead of copying it, and describe only shipped behavior.
Unfinished work belongs in a dedicated roadmap only when it has an accepted
scope; confirmed defects belong in a known-issues page; release notes describe
one shipped audience-facing revision. Move facts between those pages rather than
duplicating them.

For large, high-risk, or external-integration work, keep the accepted plan
immutable and record execution/evidence separately. Inventory each operation,
verify upstream contracts against canonical sources, use deterministic fakes,
and make real-service smoke tests explicit and opt-in. Record only reproduced
defects.

## Documentation impact map

| Change | Update in the same batch |
| --- | --- |
| User-visible behavior | Nearest user page, [Capabilities](../capabilities.md), and [README](../../README.md) if navigation changes |
| Configuration or setup | [Configuration](../configuration.md), [Setup](../setup.md), relevant deployment page, and their nearest index |
| Package ownership or architecture | [Codebase map](codebase-map.md), [Architecture](architecture.md), and this index |
| Schema, retention, encryption, or stored data | [Data](data.md), [Operations](operations.md) when operator impact changes, and nearest index |
| Tests, tools, UI states, or CI | [Validation](validation.md) and nearest index |
| Security boundary or disclosure process | [Security](../SECURITY.md), relevant architecture/data/operations page, and nearest index |
| Deployment, backup, health, rollback, or release | [Operations](operations.md), relevant public setup/configuration page, and [README](../../README.md) |

Update this map when a new canonical documentation domain is introduced.
33 changes: 33 additions & 0 deletions docs/development/operations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Operations

Run one Aperture process per state directory. The container runs without added
privileges and should receive only a writable `/config`, its listening port, and
network access to the configured media server and webhook destinations. Restrict
proxy trust to the directly connected proxy. Keep `.env`, `/config`, cookies,
invite URLs, logs, and backups private; diagnostics must be redacted before
sharing.

## Backup and restore

Before upgrades, stop Aperture and copy the entire host state directory,
including `encryption.key`, the database, and any matching WAL/SHM files. Store
the backup off-host with restricted access. Restore into a separate directory
using the matching application version; never combine a database with stale
WAL/SHM files. Verify `/healthz`, setup/login, templates, invites, registration
history, and a synthetic media-server connection before treating the backup as
usable. Record the last successful restore test.

## Release and rollback

Builds are published by `.github/workflows/docker-image.yml` after the reusable
CI workflow succeeds. Deploy an immutable version tag or digest and record it
with the backup used for the change; `latest` is not a rollback reference.
After deployment, verify process health, `/healthz`, authentication, an
administrator read, and configured outbound connectivity without exposing
secrets.

Schema upgrades are forward-only. Application rollback therefore means restoring
the pre-upgrade state with the previous immutable image. Stop on failed health or
data verification; preserve failed state for diagnosis. Maintenance or repair
actions that mutate users, registrations, or SQLite state are explicit,
operator-approved procedures, never automatic troubleshooting steps.
39 changes: 39 additions & 0 deletions docs/development/validation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Validation

Use the smallest focused check that covers the changed contract:

| Change | Focused check |
| --- | --- |
| CLI behavior | `go test ./cmd/aperture` |
| SQLite/schema | `go test ./internal/db` |
| HTTP, templates, CSS, or JavaScript | `go test ./internal/httpserver` plus browser checks below |
| Jellyfin/Emby protocol | `go test ./internal/mediaserver/...` |
| Configuration/security | `go test ./internal/config ./internal/security` |
| Entrypoint | `sh scripts/test-entrypoint.sh` |
| Release binary | `CGO_ENABLED=0 go build -trimpath -o /tmp/aperture ./cmd/aperture` |

Format touched Go files with `gofmt -w`. Performance or refactor claims require a
representative before/after benchmark or trace and a regression threshold; a
clean test run alone is not performance evidence.

## UI evidence

Rebuild before visual checks because templates, CSS, and JavaScript are embedded.
Use a disposable database and synthetic users/invites. For every affected flow,
check desktop and narrow mobile widths, keyboard-only navigation, visible focus,
labels and error association, and loading, empty, validation-error, upstream-error,
and permission-denied states. Cover setup, login, invite registration, and the
affected admin page. Do not use real credentials or production data.

## Prerequisites and final gate

Use the Go version in `go.mod`. Do not install missing tools or download
dependencies without approval. Reuse local dependencies only when `go.sum`, the
Go toolchain, OS/architecture, and installed module tree match the revision being
validated; otherwise fail closed and use a clean locked environment.

The one comprehensive gate is the complete GitHub Actions `CI` workflow in
`.github/workflows/ci.yml` for the exact revision. It checks formatting,
whitespace, `go test ./...`, the entrypoint, vet, race detection, pinned
Staticcheck and govulncheck versions, a release-style build, and the Docker image.
Do not claim release readiness until both jobs pass on that revision.
7 changes: 2 additions & 5 deletions docs/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,5 @@ installs must retain their current host data directory when adopting `.env`;
set `APERTURE_DATA_DIR` to that directory. Without this override Compose preserves
its original `/mnt/cache/appdata/aperture` mapping.

For backup, stop Aperture and copy the entire host state directory, including
`encryption.key` and any SQLite WAL/SHM files, then restart it. Restore into a
separate directory with the matching application version and verify setup/login,
templates, invites, and registration history. Never combine a restored database
with stale WAL/SHM files. Keep a pre-upgrade backup: schema upgrades are forward-only.
Before upgrades, follow the [backup, restore, and rollback
procedure](development/operations.md). Schema upgrades are forward-only.
Loading