feat(release): prepare first public preview

This commit is contained in:
Magent release tooling
2026-09-22 15:17:22 +12:00
parent 76b2b8d879
commit 27ff894f68
29 changed files with 610 additions and 103 deletions
+1
View File
@@ -5,6 +5,7 @@
!Dockerfile !Dockerfile
!.dockerignore !.dockerignore
!LICENSE !LICENSE
!THIRD_PARTY_NOTICES.md
!backend/ !backend/
!backend/requirements.txt !backend/requirements.txt
!backend/app/ !backend/app/
+1
View File
@@ -7,6 +7,7 @@
*.png binary *.png binary
*.jpg binary *.jpg binary
*.jpeg binary *.jpeg binary
*.webp binary
*.gif binary *.gif binary
*.ico binary *.ico binary
*.pdf binary *.pdf binary
+40
View File
@@ -0,0 +1,40 @@
---
name: Bug report
about: Report reproducible behavior that looks wrong
title: "[Bug] "
labels: ""
---
## What happened?
<!-- Describe the visible problem. -->
## What did you expect?
<!-- Describe the expected result. -->
## How can we reproduce it?
1.
2.
3.
## Environment
- Magent version/tag:
- Container image digest, if applicable:
- Installation method (Compose, Portainer, docker run, native):
- Host OS and CPU architecture:
- Browser and version:
- Reverse proxy, if applicable:
- Connected app and version involved:
## Sanitized diagnostics
<!-- Paste the smallest relevant error/log excerpt. Redact private addresses and user data. -->
- [ ] I searched existing issues and the troubleshooting guides.
- [ ] I reproduced this on the current public preview.
- [ ] I removed passwords, tokens, API keys, cookies, email addresses, private URLs and personal data.
- [ ] I did not attach an `.env`, database, backup or generated secrets file.
- [ ] This is not a security vulnerability. Security reports follow `SECURITY.md`.
+26
View File
@@ -0,0 +1,26 @@
---
name: Feature request
about: Suggest an improvement to Magent
title: "[Feature] "
labels: ""
---
## Problem
<!-- Who has this problem, and what are they trying to do? -->
## Desired outcome
<!-- Describe the user-visible result. -->
## Current workaround
<!-- How do you handle it today? -->
## Relevant integration
<!-- If this needs another app, include its name, version and public API docs. -->
## Additional context
<!-- Mockups and synthetic examples are welcome. Remove private data. -->
@@ -0,0 +1,29 @@
---
name: Installation help
about: Ask for help with a documented installation path
title: "[Install] "
labels: ""
---
## Goal
<!-- What are you trying to install, and where? -->
## Method and environment
- Installation guide followed:
- Host OS and CPU architecture:
- Docker/Compose/Portainer/Python/Node versions, as applicable:
- Browser-facing URL scheme (`http` or `https`; redact private hostnames if needed):
- Reverse proxy, if applicable:
## Where it stopped
<!-- Include the exact command/step and sanitized output. -->
## Checks already tried
- [ ] I checked the matching troubleshooting table.
- [ ] I confirmed the container/service health and retained the same data path.
- [ ] I removed credentials, tokens, cookies, private URLs and personal data.
- [ ] I did not attach an `.env`, database, backup or generated secrets file.
+17
View File
@@ -0,0 +1,17 @@
## What and why
<!-- Explain the user problem and the change. -->
## Validation
<!-- List exact commands/checks run and any skipped platform checks. -->
- [ ] Backend tests pass or are not affected.
- [ ] Frontend tests, lint, format, typecheck and build pass or are not affected.
- [ ] Documentation and changelog are updated where needed.
- [ ] New behavior has regression coverage.
- [ ] No credentials, user data, databases, backups or private deployment details are included.
## Compatibility
<!-- Note database, configuration, container, API or upgrade effects. -->
+36
View File
@@ -0,0 +1,36 @@
# Changelog
This file records public Magent releases. The in-application changelog contains
the shorter user-facing highlights.
## Unreleased
- No unreleased changes recorded.
## 0.1.0-preview.1 — 2026-09-22
First public preview.
### Included
- Guided first-install setup for the administrator, application URL and optional integrations.
- Request discovery, submission and status tracking across Seerr and Arr services.
- Jellyfin playback links, issue reporting and repair follow-up.
- Jellystat viewing insights, monthly reports and optional email recaps.
- Local/Jellyfin sign-in, invitations, account linking and feature controls.
- Encrypted configuration/database backups with staged, restart-only restore.
- Docker Compose, Docker CLI, Portainer, native Linux and foreground setup guides.
- Hardened non-root container defaults and a documented release smoke harness.
### Known limits
- The published image is validated for Linux/amd64 only.
- One writable SQLite instance and one backend worker are supported; HA is not.
- Native Linux services have static systemd validation, not broad distribution certification.
- This preview does not promise an LTS window or security-response SLA.
### Upgrade note
Existing installations must retain their data mount and signing/encryption keys.
Create an encrypted backup, record the running image digest, and follow the
[upgrade guidance](docs/DOCKER.md#updates-and-rollback).
+21
View File
@@ -0,0 +1,21 @@
# Repository guide
Magent combines a FastAPI backend, Next.js frontend and SQLite database. Read
`CONTRIBUTING.md` before changing code and `docs/INSTALLATION.md` before changing
deployment behavior.
## Important paths
- `backend/app/`: API, integrations, setup, persistence and background work.
- `frontend/app/`: application routes and interface components.
- `backend/tests/` and frontend `*.test.*`: synthetic regression coverage.
- `compose.yml`: public prebuilt-image deployment.
- `Dockerfile`: pinned multi-stage production build.
## Working rules
- Never read, commit or print real `.env`, database, backup or credential data.
- Keep SQLite to one backend worker and one writable application instance.
- Preserve origin checks, CSP nonces, non-root execution and persistent key/data paths.
- Add regression tests and run the checks in `CONTRIBUTING.md`.
- Use synthetic fixtures; do not call live media services from tests.
+23
View File
@@ -0,0 +1,23 @@
# Community Code of Conduct
Magent's community should be practical, welcoming and safe for people with
different backgrounds and levels of experience.
## Expected behavior
- Be respectful and specific when discussing code, ideas or mistakes.
- Assume good intent while accepting corrections and evidence.
- Give useful context and protect other people's privacy.
- Welcome questions without mocking a person's experience level.
- Keep feedback about the work, not the person.
Harassment, threats, discrimination, sexualized attention, deliberate exposure
of private information, and repeated disruptive behavior are not acceptable.
Maintainers may edit or remove contributions and temporarily or permanently
restrict participation when needed to protect the community. Non-sensitive
conduct concerns can be raised through the issue tracker. The public preview
does not yet publish a monitored private conduct-reporting channel; do not put
private evidence or personal information in a public issue.
This code applies in project spaces and when representing the project elsewhere.
+65
View File
@@ -0,0 +1,65 @@
# Contributing to Magent
Thanks for helping improve Magent. Small, reviewable changes with a clear reason
are the easiest to test and merge.
## Before opening an issue
- Use [SUPPORT.md](SUPPORT.md) to choose the right report type.
- Search existing issues and read the relevant installation guide.
- Remove credentials, tokens, email addresses, database content and private URLs.
- Use [SECURITY.md](SECURITY.md) for vulnerabilities; do not publish exploit details.
## Development setup
Use Python 3.14 and Node.js 24. Follow
[the foreground development guide](docs/LOCAL_DEVELOPMENT.md), use synthetic
data, and never connect tests to someone else's services.
## Quality checks
Backend:
```sh
python -m venv .venv
. .venv/bin/activate
pip install -r backend/requirements-dev.txt
python -m unittest discover -s backend/tests -p 'test_*.py'
python scripts/check_environment_docs.py
```
Frontend:
```sh
cd frontend
npm ci
npm test
npm run lint
npm run format:check
npm run typecheck
npm run build
```
Container changes should also pass both isolated smoke modes:
```sh
docker build -t magent:review .
bash scripts/ci_container_smoke.sh magent:review
MAGENT_SMOKE_MANAGED=true bash scripts/ci_container_smoke.sh magent:review
```
The smoke harness creates disposable containers, networks and volumes. Do not
aim it at a live instance or reuse a production volume.
## Pull requests
1. Base public work on the `release` branch.
2. Keep each pull request focused and explain the user-visible reason.
3. Add or update tests for behavior changes.
4. Update documentation and `CHANGELOG.md` when behavior or setup changes.
5. State exactly which checks ran and any platform limits or skipped checks.
6. Do not commit generated builds, `.env` files, logs, databases, backups or secrets.
By contributing, you agree that your contribution is licensed under the project's
[MIT License](LICENSE). Participation must follow the
[Code of Conduct](CODE_OF_CONDUCT.md).
+8 -2
View File
@@ -54,6 +54,8 @@ RUN node --version
ARG MAGENT_UID=1000 ARG MAGENT_UID=1000
ARG MAGENT_GID=1000 ARG MAGENT_GID=1000
ARG MAGENT_VERSION=0.1.0-preview.1
ARG MAGENT_REVISION=unknown
RUN addgroup -g ${MAGENT_GID} magent \ RUN addgroup -g ${MAGENT_GID} magent \
&& adduser -D -u ${MAGENT_UID} -G magent -s /sbin/nologin magent \ && adduser -D -u ${MAGENT_UID} -G magent -s /sbin/nologin magent \
&& install -d -o magent -g magent -m 0700 /app/data \ && install -d -o magent -g magent -m 0700 /app/data \
@@ -76,12 +78,16 @@ COPY --chown=magent:magent --from=frontend-builder /frontend/.next/static /app/f
COPY --chown=magent:magent --from=frontend-builder /frontend/public /app/frontend/public COPY --chown=magent:magent --from=frontend-builder /frontend/public /app/frontend/public
COPY docker/supervisord.conf /etc/supervisor/conf.d/magent.conf COPY docker/supervisord.conf /etc/supervisor/conf.d/magent.conf
COPY LICENSE /usr/share/licenses/magent/LICENSE COPY --chown=magent:magent LICENSE /usr/share/licenses/magent/LICENSE
COPY --chown=magent:magent THIRD_PARTY_NOTICES.md /usr/share/licenses/magent/THIRD_PARTY_NOTICES.md
COPY --from=frontend-builder /licenses /usr/share/licenses/magent/frontend COPY --from=frontend-builder /licenses /usr/share/licenses/magent/frontend
LABEL org.opencontainers.image.title="Magent" \ LABEL org.opencontainers.image.title="Magent" \
org.opencontainers.image.description="Self-hosted media requests, issues and viewing insights" \ org.opencontainers.image.description="Self-hosted media requests, issues and viewing insights" \
org.opencontainers.image.licenses="MIT" org.opencontainers.image.licenses="MIT" \
org.opencontainers.image.source="https://git.amslabs.net/Rephl3x/Magent-Public" \
org.opencontainers.image.version="${MAGENT_VERSION}" \
org.opencontainers.image.revision="${MAGENT_REVISION}"
USER magent:magent USER magent:magent
+108 -75
View File
@@ -1,78 +1,111 @@
# Magent # Magent
Self-hosted media requests, viewing stats and issue management for Jellyfin, Magent is a self-hosted home for media requests, viewing insights and issue
Seerr, Sonarr, Radarr and related services. Magent combines a Python/FastAPI API, follow-up. It sits in front of Jellyfin, Seerr, Sonarr, Radarr and related apps,
a Next.js frontend and SQLite. Run the prebuilt non-root container or install then turns their technical state into a clearer experience for the people using
the Python and Node.js services directly—Portainer is optional. your server.
## Install **Public preview:** `0.1.0-preview.1` · [Source code](https://git.amslabs.net/Rephl3x/Magent-Public) ·
[Docker Hub](https://hub.docker.com/r/rephl3xnz/magent) · [MIT license](LICENSE)
Start with the [installation guide](docs/INSTALLATION.md) to choose a method: ![Magent's first-run setup wizard](docs/images/setup-wizard.webp)
| Method | Instructions | Magent does not store or stream media, replace your existing applications, or
| --- | --- | provide content. You bring and operate those services; Magent connects to them.
| Docker Compose or `docker run` | [Docker installation](docs/DOCKER.md) |
| Portainer | [Single-file stack](docs/PORTAINER.md) |
| Linux without Docker | [Native install and systemd services](docs/NATIVE_INSTALL.md) |
| Windows/macOS/Linux foreground | [Source installation and development](docs/LOCAL_DEVELOPMENT.md) |
For a fresh **Docker Compose** install, download [compose.yml](compose.yml) into ## Why Magent?
its own deployment directory and run:
Media automation is powerful, but its status messages are written for operators.
Magent gives users one place to request something, understand what is happening,
see when it is ready, report a problem and follow the response.
- Search for and submit movie or TV requests through Seerr.
- Follow a request across approval, Arr monitoring, download and Jellyfin availability.
- Open ready titles in Jellyfin and report playback or library problems.
- View private Jellystat-powered viewing insights and monthly reports.
- Offer local or Jellyfin sign-in, invite-only registration and per-user features.
- Manage integrations, users, branding, notifications and diagnostics from the UI.
- Export an encrypted backup of configuration, SQLite data and optional artwork.
## Current scope
- The published container is validated on **Linux/amd64**.
- Magent is a single-instance application using one writable SQLite database. It
is not an HA, Kubernetes or multi-replica deployment.
- Native Windows foreground operation has been smoke-tested. Linux systemd files
are statically validated; macOS instructions are reviewed but not runtime-tested.
- This is an early public release. Pin an immutable image tag or digest, retain
backups, and test upgrades before using them on your main instance.
See [release notes](CHANGELOG.md) and [installation support boundaries](docs/INSTALLATION.md#platform-and-support-boundaries).
## Quick start with Docker Compose
Docker Compose is the recommended first installation. Create an empty directory,
download the public Compose file, and start Magent:
```sh ```sh
curl --fail --location --output compose.yml \
https://git.amslabs.net/Rephl3x/Magent-Public/raw/branch/release/compose.yml
docker compose -f compose.yml -p magent pull
docker compose -f compose.yml -p magent up -d --wait --wait-timeout 120
docker compose -f compose.yml -p magent exec --user magent magent \
python -m app.container_bootstrap setup-token
```
PowerShell:
```powershell
Invoke-WebRequest -Uri 'https://git.amslabs.net/Rephl3x/Magent-Public/raw/branch/release/compose.yml' -OutFile compose.yml
docker compose -f compose.yml -p magent pull docker compose -f compose.yml -p magent pull
docker compose -f compose.yml -p magent up -d --wait --wait-timeout 120 docker compose -f compose.yml -p magent up -d --wait --wait-timeout 120
docker compose -f compose.yml -p magent ps
docker compose -f compose.yml -p magent exec --user magent magent python -m app.container_bootstrap setup-token docker compose -f compose.yml -p magent exec --user magent magent python -m app.container_bootstrap setup-token
``` ```
Keep that directory/project name and its persistent volume unchanged for updates. Open `http://SERVER_IP:3000/setup`, enter the console-only token and confirm the
This uses `rephl3xnz/magent:latest` with no environment variables, local source browser-facing URL. Then create the first administrator and connect the apps you
build or Dockerfile. Portainer users can paste the same file into a Docker use. Optional integrations can be skipped. The managed image creates and retains
Standalone stack instead. Native users follow their separate guide for explicit its own private keys; this path needs no `.env` or Dockerfile.
environment configuration and service management.
**Image availability:** the managed-install image is published on Docker Hub. If you cloned the repository, the optional helper exposes the same operations:
Only Linux/amd64 has been validated. `latest` is mutable; record the resolved `./setup.sh check`, `./setup.sh install`, and `./setup.sh token`. Read a script
image digest before updating, or pin an immutable release tag. before running it; the documented Compose commands remain the canonical path.
1. Wait for the container to become healthy and retrieve the token using the For public access, configure DNS and HTTPS before creating the administrator.
command above (or the Portainer console instructions in its guide). Do not forward the plain HTTP port directly to the internet.
2. Open the Docker host's address on port 3000. Confirm the browser-facing URL
in setup and use the token to create the first administrator.
The **Get setup token** button shows the console instructions and lets you
copy the command; it never reveals the token to public visitors.
3. Connect your apps, choose preferences and finish setup. Optional apps can
be skipped. Save an encrypted backup afterwards.
Keep the Compose security block unchanged. Database storage is fixed at ## Other installation methods
`/app/data/magent.db` and API docs are disabled in managed installs. CORS and
cookie security follow the confirmed URL. Use HTTPS before public access.
See [all installation methods](docs/INSTALLATION.md), | Method | Guide |
[all environment options](docs/ENVIRONMENT.md), | --- | --- |
[backup and restore](docs/installation-and-recovery.md) and | Docker Compose | [Complete Docker guide](docs/DOCKER.md#docker-compose) |
[advanced installation/upgrades](docs/PUBLIC_RELEASE.md). | Docker CLI without Compose | [`docker run` guide](docs/DOCKER.md#docker-cli-without-compose) |
Existing installations must retain their original data volume and signing/ | Portainer | [Paste-and-deploy stack](docs/PORTAINER.md) |
encryption keys; this fresh-install template is not an automatic migration. | Build the container from source | [Source-build override](docs/DOCKER.md#build-the-container-from-source) |
| Native Linux | [Python/Node and systemd](docs/NATIVE_INSTALL.md) |
| Windows, macOS or Linux foreground | [Local/source installation](docs/LOCAL_DEVELOPMENT.md) |
## Build and test Start with the [installation overview](docs/INSTALLATION.md) if you are unsure.
The [environment reference](docs/ENVIRONMENT.md) documents every supported
variable, while [backup and recovery](docs/installation-and-recovery.md) covers
portable exports, offline snapshots and safe restore.
The source tree contains everything needed to build the application: ### Existing installations
Keep the same Compose project name, data mount and signing/encryption keys. The
fresh-install named volume is not an automatic migration of an existing bind
mount. Record your working image digest and make an encrypted backup before an
upgrade. Never use `docker compose down -v` on data you want to keep.
## Build and test from source
```sh ```sh
git clone --branch release --single-branch https://git.amslabs.net/Rephl3x/Magent-Public.git
cd Magent-Public
docker compose -f compose.yml -f compose.build.yml -p magent up -d --build docker compose -f compose.yml -f compose.build.yml -p magent up -d --build
``` ```
For a disposable verification run, without touching an existing installation: The full quality gate uses Python 3.14 and Node.js 24:
```sh
docker build -t magent:review .
bash scripts/ci_container_smoke.sh magent:review
MAGENT_SMOKE_MANAGED=true bash scripts/ci_container_smoke.sh magent:review
```
Unit checks require Python 3.14 and Node 24:
```sh ```sh
python -m venv .venv python -m venv .venv
@@ -86,37 +119,37 @@ npm test
npm run lint npm run lint
npm run format:check npm run format:check
npm run typecheck npm run typecheck
npm run build
``` ```
On Windows, activate `.venv\Scripts\Activate.ps1` instead. Do not point tests On Windows, use `.venv\Scripts\Activate.ps1`. Tests must use synthetic data and
at live services or use production credentials. must never point at live media services.
## How it is organised ## Project layout
- `backend/app/routers/`: authenticated API endpoints and administration. - `backend/app/`: FastAPI routes, service clients, request state, authentication,
- `backend/app/clients/`: media-service clients; `services/`: request states, setup, notifications and encrypted backups.
synchronisation, notifications, setup and encrypted backups. - `frontend/app/`: Next.js pages, shared UI, account portal and administration.
- `backend/app/db.py` and `schema_migrations.py`: SQLite persistence/migrations.
- `frontend/app/`: pages and shared interface components; `frontend/proxy.ts`:
browser security headers and request nonces.
- `backend/tests/` and frontend `*.test.*`: synthetic regression tests. - `backend/tests/` and frontend `*.test.*`: synthetic regression tests.
- `Dockerfile` and `docker/`: multi-stage build and process supervision. - `compose.yml`: prebuilt-image install; `compose.build.yml`: source-build override.
- `compose.yml`: prebuilt-image install; `compose.build.yml`: source override. - `deploy/native/`: example Linux service units and private configuration templates.
- `deploy/native/`: example Linux service units and backend/frontend configuration. - `docs/`: installation, environment, HTTPS, backup, upgrade and rollback guidance.
Requests are cached from Seerr, joined to collector/download/library evidence, The `release` branch is a deliberately small public export. It excludes private
normalised into a user-facing state and displayed by the frontend. App settings deployment automation, credentials, runtime data and internal Git history.
are stored in SQLite; sensitive settings are encrypted with installation-specific
keys. Integrations are optional and are configured through the setup wizard.
This `release` branch intentionally excludes internal deployment scripts, ## Help, feedback and contributions
environment files, runtime data, development reports and prior Git history.
It contains no workflow that automatically deploys or publishes an image.
## Contributing and security - Read [SUPPORT.md](SUPPORT.md) before opening an installation or bug report.
- See [CONTRIBUTING.md](CONTRIBUTING.md) for development and pull-request checks.
- Report security concerns using [SECURITY.md](SECURITY.md); never post secrets or
exploit details in a public issue.
- Community participation follows [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
Keep changes focused, add regression tests and run the checks above. Never Magent was developed through an iterative, AI-assisted workflow and then checked
commit tokens, database exports, backups or real user information. with automated regression tests, isolated container smoke tests and a sanitized
See [SECURITY.md](SECURITY.md) for reporting guidance and deployment precautions. public-release pass. Treat generated code like any other code: inspect it, test
it and report anything that does not make sense.
Licensed under [MIT](LICENSE). Third-party dependency licences remain applicable. Magent is licensed under the [MIT License](LICENSE). Dependencies and base-image
components retain their own licenses; see [third-party notices](THIRD_PARTY_NOTICES.md).
+10 -5
View File
@@ -4,11 +4,16 @@
Do not post passwords, access tokens, encryption keys, database exports, backup Do not post passwords, access tokens, encryption keys, database exports, backup
files or live exploit details in public issues, discussions or container logs. files or live exploit details in public issues, discussions or container logs.
Use the repository hosting platform's private vulnerability-reporting feature Non-sensitive security hardening reports may be filed at
if the release owner has enabled it. Otherwise contact the maintainer privately https://git.amslabs.net/Rephl3x/Magent-Public/issues/new after removing all
through the platform where you obtained this release before sending sensitive deployment details and secrets. The public preview does not yet publish a
details. This repository does not currently advertise a dedicated reporting monitored confidential-reporting channel. Do not put embargoed vulnerabilities,
address; the release owner must establish one before a broad public launch. live exploits or sensitive evidence in a public issue. If a report cannot be
made safely in public, retain it until a private contact is listed here.
Reports are handled on a best-effort basis. The project does not promise a
specific response time or security-support window. The newest public preview is
the only version considered for security fixes unless a release note says otherwise.
Include the image tag/digest, affected version, a minimal reproduction using Include the image tag/digest, affected version, a minimal reproduction using
synthetic data, and the security impact. Remove deployment credentials and synthetic data, and the security impact. Remove deployment credentials and
+31
View File
@@ -0,0 +1,31 @@
# Support
Magent is an early public release maintained on a best-effort basis. There is no
paid support or guaranteed response time.
## Start here
- Installation choice: [docs/INSTALLATION.md](docs/INSTALLATION.md)
- Docker and Compose: [docs/DOCKER.md](docs/DOCKER.md)
- Portainer: [docs/PORTAINER.md](docs/PORTAINER.md)
- Native Linux: [docs/NATIVE_INSTALL.md](docs/NATIVE_INSTALL.md)
- Configuration: [docs/ENVIRONMENT.md](docs/ENVIRONMENT.md)
- Backup and restore: [docs/installation-and-recovery.md](docs/installation-and-recovery.md)
## Ask for help or report a problem
Use the matching issue template at
https://git.amslabs.net/Rephl3x/Magent-Public/issues/new.
Include the Magent version/tag and image digest, CPU architecture, installation
method, browser, exact reproduction steps, expected result and sanitized logs.
Say whether the problem survives a normal restart. Never attach `.env` files,
databases, backups, generated secret files, API keys, passwords, access tokens,
private URLs or personal viewing data.
Feature requests should describe the problem and intended user, not only a
proposed implementation. Integration requests should link to the upstream
application's public API documentation.
For a vulnerability, follow [SECURITY.md](SECURITY.md) instead of opening a
detailed public report.
+27
View File
@@ -0,0 +1,27 @@
# Third-party notices
Magent's original source is licensed under the [MIT License](LICENSE). Third-party
software and assets retain their own copyright and license terms.
The exact JavaScript dependency graph and resolved versions are recorded in
`frontend/package-lock.json`. Production runtime packages include Next.js,
React, React DOM, Sharp and their transitive dependencies. Their license terms
come from the installed package metadata and license files, not from fields in
the lockfile. The release build collects discoverable production package
license and notice files under `/usr/share/licenses/magent/frontend`.
The exact direct Python versions are recorded in `backend/requirements.txt` and
`docker/requirements-runtime.txt`. Python package metadata and license files are
installed with those packages and remain subject to their respective terms.
The release container also contains components from its pinned Python, Node.js
and Alpine base images. Those components are not relicensed by Magent. The image
includes this notice, Magent's MIT license, Node.js's license, installed Python
package license files, discoverable production frontend license/notice files,
and any license files supplied by the Alpine packages in the runtime image under
`/usr/share/licenses`.
Before redistributing a modified image, regenerate and review its software bill
of materials and preserve every applicable license, copyright and notice file.
Lockfiles are the version inventory; this summary is not a replacement for the
complete license texts supplied by each dependency.
+5 -2
View File
@@ -1,2 +1,5 @@
BUILD_NUMBER = "0803262237" BUILD_NUMBER = "0.1.0-preview.1"
CHANGELOG = '2026-09-19|Initial minimal release source snapshot' CHANGELOG = """2026-09-22|First public preview
2026-09-22|Guided setup for Docker, Portainer and native installs
2026-09-22|Encrypted backup and staged restore
2026-09-22|Requests, viewing insights, invites and issue follow-up"""
+2 -2
View File
@@ -515,7 +515,7 @@ class SiteInfoTests(unittest.TestCase):
"site_banner_tone": "warning", "site_banner_tone": "warning",
"site_banner_background_color": "#123ABC", "site_banner_background_color": "#123ABC",
"site_banner_border_color": "red", "site_banner_border_color": "red",
"site_login_message": "Use your Grizzlyflix account to sign in.", "site_login_message": "Use your ExampleFlix account to sign in.",
}) })
with patch.object(site_router, "get_runtime_settings", return_value=runtime): with patch.object(site_router, "get_runtime_settings", return_value=runtime):
@@ -523,7 +523,7 @@ class SiteInfoTests(unittest.TestCase):
self.assertEqual(info["banner"]["backgroundColor"], "#123abc") self.assertEqual(info["banner"]["backgroundColor"], "#123abc")
self.assertIsNone(info["banner"]["borderColor"]) self.assertIsNone(info["banner"]["borderColor"])
self.assertEqual(info["login"]["message"], "Use your Grizzlyflix account to sign in.") self.assertEqual(info["login"]["message"], "Use your ExampleFlix account to sign in.")
class SiteSettingValidationTests(unittest.IsolatedAsyncioTestCase): class SiteSettingValidationTests(unittest.IsolatedAsyncioTestCase):
+1 -1
View File
@@ -41,7 +41,7 @@ class IssueAcceptanceTests(TempDatabaseMixin, unittest.IsolatedAsyncioTestCase):
content = email.await_args.kwargs content = email.await_args.kwargs
self.assertEqual(content["subject"], f"Ready to try again? Magent issue #{item['id']}") self.assertEqual(content["subject"], f"Ready to try again? Magent issue #{item['id']}")
self.assertIn("affected content in Jellyfin", content["body_text"]) self.assertIn("affected content in Jellyfin", content["body_text"])
self.assertNotIn("grizzlyflix", content["body_html"].lower()) self.assertNotIn("exampleflix", content["body_html"].lower())
self.assertIn("YES — it works", content["body_html"]) self.assertIn("YES — it works", content["body_html"])
self.assertIn("NO — still broken", content["body_html"]) self.assertIn("NO — still broken", content["body_html"])
self.assertIn(f"/issues/confirm/{item['id']}#yes", content["body_html"]) self.assertIn(f"/issues/confirm/{item['id']}#yes", content["body_html"])
+3 -3
View File
@@ -93,7 +93,7 @@ class NewsletterConsentTests(NewsletterFixture, unittest.IsolatedAsyncioTestCase
rendered = sender.call_args.args[1] rendered = sender.call_args.args[1]
self.assertEqual(rendered['subject'], 'Confirm your Magent newsletter subscription') self.assertEqual(rendered['subject'], 'Confirm your Magent newsletter subscription')
self.assertIn('NEW IN YOUR LIBRARY', rendered['body_html']) self.assertIn('NEW IN YOUR LIBRARY', rendered['body_html'])
self.assertNotIn('grizzlyflix', rendered['body_html'].lower()) self.assertNotIn('exampleflix', rendered['body_html'].lower())
self.assertNotIn('Arrival', rendered['body_html']) self.assertNotIn('Arrival', rendered['body_html'])
url = re.search(r'https://[^\s]+', rendered['body_text']).group(0) url = re.search(r'https://[^\s]+', rendered['body_text']).group(0)
self.assertEqual(urlsplit(url).path, '/newsletter-subscription') self.assertEqual(urlsplit(url).path, '/newsletter-subscription')
@@ -393,7 +393,7 @@ class NewsletterDeliveryTests(NewsletterFixture, unittest.IsolatedAsyncioTestCas
self.assertEqual(draft['intro'], custom_intro) self.assertEqual(draft['intro'], custom_intro)
def test_generic_email_template_preserves_custom_subject_and_intro(self): def test_generic_email_template_preserves_custom_subject_and_intro(self):
custom_subject = 'Grizzlyflix weekend discoveries' custom_subject = 'ExampleFlix weekend discoveries'
custom_intro = 'Welcome to our own <media> community.' custom_intro = 'Welcome to our own <media> community.'
rendered = template.render( rendered = template.render(
{**content(), 'subject': custom_subject, 'intro': custom_intro}, {}, {**content(), 'subject': custom_subject, 'intro': custom_intro}, {},
@@ -406,7 +406,7 @@ class NewsletterDeliveryTests(NewsletterFixture, unittest.IsolatedAsyncioTestCas
self.assertIn('Welcome to our own &lt;media&gt; community.', rendered['body_html']) self.assertIn('Welcome to our own &lt;media&gt; community.', rendered['body_html'])
self.assertIn('Watch on Jellyfin', rendered['body_html']) self.assertIn('Watch on Jellyfin', rendered['body_html'])
self.assertIn('Explore Jellyfin', rendered['body_html']) self.assertIn('Explore Jellyfin', rendered['body_html'])
self.assertNotIn('grizzlyflix', rendered['body_html'].lower()) self.assertNotIn('exampleflix', rendered['body_html'].lower())
async def test_weekly_worker_collects_once_and_delivers_to_confirmed_subscriber(self): async def test_weekly_worker_collects_once_and_delivers_to_confirmed_subscriber(self):
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
+4 -4
View File
@@ -64,7 +64,7 @@ container in either case, not a native Windows-container or macOS application.
### 1. Save the deployment file ### 1. Save the deployment file
Create a new directory for this installation and save the release branch's Create a new directory for this installation and save the release branch's
[raw compose.yml](https://git.amslabs.net/Rephl3x/Magent/raw/branch/release/compose.yml) [raw compose.yml](https://git.amslabs.net/Rephl3x/Magent-Public/raw/branch/release/compose.yml)
there. Keep your customized copy for future operations instead of overwriting there. Keep your customized copy for future operations instead of overwriting
it when updating. You need only this file for the prebuilt image. it when updating. You need only this file for the prebuilt image.
@@ -73,7 +73,7 @@ Linux, macOS or WSL:
```sh ```sh
mkdir magent-install mkdir magent-install
cd magent-install cd magent-install
curl --fail --location --output compose.yml https://git.amslabs.net/Rephl3x/Magent/raw/branch/release/compose.yml curl --fail --location --output compose.yml https://git.amslabs.net/Rephl3x/Magent-Public/raw/branch/release/compose.yml
``` ```
PowerShell: PowerShell:
@@ -81,7 +81,7 @@ PowerShell:
```powershell ```powershell
New-Item -ItemType Directory -Path magent-install New-Item -ItemType Directory -Path magent-install
Set-Location magent-install Set-Location magent-install
Invoke-WebRequest -Uri 'https://git.amslabs.net/Rephl3x/Magent/raw/branch/release/compose.yml' -OutFile compose.yml Invoke-WebRequest -Uri 'https://git.amslabs.net/Rephl3x/Magent-Public/raw/branch/release/compose.yml' -OutFile compose.yml
``` ```
Alternatively, open the raw link in your browser and save the plain text as Alternatively, open the raw link in your browser and save the plain text as
@@ -236,7 +236,7 @@ It requires the full source tree and Docker build support; the prebuilt-image
installation does not. Use a separate checkout directory: installation does not. Use a separate checkout directory:
```sh ```sh
git clone --branch release --single-branch https://git.amslabs.net/Rephl3x/Magent.git Magent-source git clone --branch release --single-branch https://git.amslabs.net/Rephl3x/Magent-Public.git Magent-source
cd Magent-source cd Magent-source
docker compose -f compose.yml -f compose.build.yml -p magent config --quiet docker compose -f compose.yml -f compose.build.yml -p magent config --quiet
docker compose -f compose.yml -f compose.build.yml -p magent build magent docker compose -f compose.yml -f compose.build.yml -p magent build magent
+4
View File
@@ -238,6 +238,8 @@ consumer; builder values and supervisor values are not user-editable app setting
| `PYTHONUNBUFFERED` | Image: `1` | Flush Python stdout/stderr without buffering. | | `PYTHONUNBUFFERED` | Image: `1` | Flush Python stdout/stderr without buffering. |
| `MAGENT_UID` | Build argument: `1000` | Image runtime user's UID. Not a runtime environment switch; rebuilding with a different UID also requires matching volume/tmpfs ownership. | | `MAGENT_UID` | Build argument: `1000` | Image runtime user's UID. Not a runtime environment switch; rebuilding with a different UID also requires matching volume/tmpfs ownership. |
| `MAGENT_GID` | Build argument: `1000` | Image runtime user's GID; same ownership caveat as UID. | | `MAGENT_GID` | Build argument: `1000` | Image runtime user's GID; same ownership caveat as UID. |
| `MAGENT_VERSION` | Build argument: `0.1.0-preview.1` | OCI image version label. Release builds set this to their public version; it does not change runtime data. |
| `MAGENT_REVISION` | Build argument: `unknown` | OCI image source-revision label. Release builds set this to the full source commit. |
## Manual Compose and test tooling ## Manual Compose and test tooling
@@ -249,6 +251,8 @@ verification tools, not the zero-input public Compose install.
| `MAGENT_IMAGE` | Required by `docker-compose.hub.yml` | Explicit published image tag/digest for manual-secret installations. | | `MAGENT_IMAGE` | Required by `docker-compose.hub.yml` | Explicit published image tag/digest for manual-secret installations. |
| `MAGENT_BIND_ADDRESS` | `127.0.0.1` | Manual template frontend bind address. | | `MAGENT_BIND_ADDRESS` | `127.0.0.1` | Manual template frontend bind address. |
| `MAGENT_HTTP_PORT` | `3000` | Manual template frontend host port. | | `MAGENT_HTTP_PORT` | `3000` | Manual template frontend host port. |
| `MAGENT_COMPOSE_FILE` | `compose.yml` | Optional `setup.sh` Compose filename override. Use a trusted local file. |
| `MAGENT_PROJECT_NAME` | `magent` | Optional `setup.sh` Compose project name. Keep it stable to retain the same named volume. |
| `PYTHON_BIN` | `python3` | Interpreter used by the backend quality gate. | | `PYTHON_BIN` | `python3` | Interpreter used by the backend quality gate. |
| `MAGENT_IMAGE_MAX_MB` | `350` | Container smoke-test unpacked image size budget in MiB. | | `MAGENT_IMAGE_MAX_MB` | `350` | Container smoke-test unpacked image size budget in MiB. |
| `MAGENT_SMOKE_MANAGED` | `false` | Test generated managed secrets when true, or synthetic manual keys when false. | | `MAGENT_SMOKE_MANAGED` | `false` | Test generated managed secrets when true, or synthetic manual keys when false. |
+2 -2
View File
@@ -15,7 +15,7 @@ connect a development checkout to production credentials/databases.
Linux/macOS (Bash/zsh): Linux/macOS (Bash/zsh):
```sh ```sh
git clone --branch release --single-branch https://git.amslabs.net/Rephl3x/Magent.git magent git clone --branch release --single-branch https://git.amslabs.net/Rephl3x/Magent-Public.git magent
cd magent cd magent
python3.14 -m venv .venv python3.14 -m venv .venv
.venv/bin/python -m pip install -r backend/requirements.txt .venv/bin/python -m pip install -r backend/requirements.txt
@@ -24,7 +24,7 @@ python3.14 -m venv .venv
Windows (PowerShell, with Python's `py` launcher installed): Windows (PowerShell, with Python's `py` launcher installed):
```powershell ```powershell
git clone --branch release --single-branch https://git.amslabs.net/Rephl3x/Magent.git magent git clone --branch release --single-branch https://git.amslabs.net/Rephl3x/Magent-Public.git magent
Set-Location magent Set-Location magent
py -3.14 -m venv .venv py -3.14 -m venv .venv
& .\.venv\Scripts\python.exe -m pip install -r backend\requirements.txt & .\.venv\Scripts\python.exe -m pip install -r backend\requirements.txt
+1 -1
View File
@@ -47,7 +47,7 @@ sudo useradd --system --user-group --create-home --home-dir /var/lib/magent --sh
sudo install -d -o magent -g magent -m 0755 /opt/magent sudo install -d -o magent -g magent -m 0755 /opt/magent
sudo install -d -o root -g root -m 0700 /etc/magent sudo install -d -o root -g root -m 0700 /etc/magent
sudo install -d -o magent -g magent -m 0700 /var/lib/magent/data sudo install -d -o magent -g magent -m 0700 /var/lib/magent/data
sudo -u magent git clone --branch release --single-branch https://git.amslabs.net/Rephl3x/Magent.git /opt/magent sudo -u magent git clone --branch release --single-branch https://git.amslabs.net/Rephl3x/Magent-Public.git /opt/magent
sudo -u magent git -C /opt/magent rev-parse HEAD sudo -u magent git -C /opt/magent rev-parse HEAD
sudo -u magent python3.14 -m venv /opt/magent/.venv sudo -u magent python3.14 -m venv /opt/magent/.venv
sudo -u magent /opt/magent/.venv/bin/python -m pip install -r /opt/magent/backend/requirements.txt sudo -u magent /opt/magent/.venv/bin/python -m pip install -r /opt/magent/backend/requirements.txt
+96
View File
@@ -0,0 +1,96 @@
# Reddit launch post
For `r/vibecoding`, use the **Vibe-Coded Projects** category/flair and retain the
build-process section below. The community's current self-promotion guidance says
project posts need educational detail about tools, workflow and lessons rather
than only a link. Recheck its rules immediately before posting. Add sanitized
screenshots if available; never show real users, domains, libraries, service URLs
or credentials.
## Suggested title
I vibe-coded Magent: a self-hosted request, stats and issue portal for Jellyfin stacks
## Alternative titles
- Magent is ready for testers — Docker, Portainer and native installs
- Sharing Magent: one place for media requests, viewing stats and issue follow-up
## Post
Hey everyone,
I’ve been vibe-coding a self-hosted project called **Magent**, and its first
public preview is ready for people to inspect, install and break:
https://git.amslabs.net/Rephl3x/Magent-Public
Magent is the user-facing layer I wanted around a Jellyfin media stack. It brings
requests, request progress, viewing stats and issue follow-up into one place,
while Jellyfin, Seerr, Sonarr, Radarr and the other connected apps keep doing the
jobs they already do.
What it currently includes:
- Search for and submit movie or TV requests through Seerr
- A plain-English request timeline across approval, Arr, downloads and Jellyfin
- “Watch now” links when something is available
- Issue reporting and repair follow-up
- Personal Jellystat viewing insights and monthly reports
- Local/Jellyfin sign-in, invitations and per-user feature controls
- Encrypted backup and restore for configuration and the SQLite database
It does **not** provide, store or stream media. You need to operate and connect
your own services.
Docker Compose is the recommended setup, but the repository also documents
Portainer, plain `docker run`, source-built containers, native Linux/systemd and
foreground Windows/macOS/Linux installs. A fresh managed Docker install creates
its own private keys and setup token, then walks you through the administrator
and app connections in the browser.
Docker image: https://hub.docker.com/r/rephl3xnz/magent
This is deliberately labelled a public preview. The container is currently
validated on Linux/amd64, Magent expects one writable SQLite instance, and it is
not presented as an HA/Kubernetes deployment. The repo documents HTTPS, reverse
proxies, origins/cookies, backups, upgrades and rollback rather than pretending
those details disappear.
The project grew through an iterative AI-assisted/vibe-coding workflow, followed
by regression tests, isolated container smoke tests and a separate sanitization
pass for the public repository. I’m sharing the source under MIT because I want
the implementation to be inspectable, not just the result.
### How I built it
My workflow was much closer to directing a small engineering loop than writing
one giant prompt. I described a user problem, had Codex work across the FastAPI,
Next.js and Docker layers, then reviewed the result in a running Beta instance.
Screenshots and real failure reports became the next round of requirements.
The useful discipline was making every vibe pass leave evidence behind. Backend
behavior got regression tests, interface changes got browser checks, and the
container got a disposable smoke harness covering fresh setup, login, security
headers, assets, encrypted backup/restore, restart and recreation. I also used
the ECC Codex plugin as a structured security/release review rather than treating
“it works on my stack” as a release gate.
The biggest release lesson was that a Git branch is not a privacy boundary. My
first idea was a sanitized public branch alongside private Beta/Prod branches.
Before launch I caught that every branch and old tag in a public repository is
still public, so I split the three-commit sanitized export into its own repository.
That was more valuable than another round of UI polish.
The most useful early feedback would be:
- where setup assumes too much knowledge
- installation steps that are unclear or wrong on your platform
- integrations that behave differently on another stack
- request, stats or issue flows that confuse real users
- backup or restore problems
Please use synthetic examples and sanitized logs in public reports—no tokens,
databases, backup files or private service details.
Repository: https://git.amslabs.net/Rephl3x/Magent-Public
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

+3 -3
View File
@@ -16,7 +16,7 @@ describe("portable default branding", () => {
expect(html).toContain("Welcome to our library"); expect(html).toContain("Welcome to our library");
expect(html).toContain("A custom message"); expect(html).toContain("A custom message");
expect(html).toContain("Local help"); expect(html).toContain("Local help");
expect(html).not.toMatch(/grizzlyflix/i); expect(html).not.toMatch(/exampleflix/i);
expect(html).not.toContain(">Beta<"); expect(html).not.toContain(">Beta<");
}); });
@@ -25,13 +25,13 @@ describe("portable default branding", () => {
expect(html).toContain("MAGENT"); expect(html).toContain("MAGENT");
expect(html).toContain("Your media member portal"); expect(html).toContain("Your media member portal");
expect(html).not.toMatch(/grizzlyflix/i); expect(html).not.toMatch(/exampleflix/i);
}); });
it("explains the Jellyfin integration without a deployment-specific service name", () => { it("explains the Jellyfin integration without a deployment-specific service name", () => {
const html = renderToStaticMarkup(<HowItWorksPage />); const html = renderToStaticMarkup(<HowItWorksPage />);
expect(html).toContain("Jellyfin is where you watch them."); expect(html).toContain("Jellyfin is where you watch them.");
expect(html).not.toMatch(/grizzlyflix/i); expect(html).not.toMatch(/exampleflix/i);
}); });
}); });
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "magent-frontend", "name": "magent-frontend",
"version": "0803262237", "version": "0.1.0-preview.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "magent-frontend", "name": "magent-frontend",
"version": "0803262237", "version": "0.1.0-preview.1",
"dependencies": { "dependencies": {
"next": "16.3.5", "next": "16.3.5",
"react": "19.2.4", "react": "19.2.4",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "magent-frontend", "name": "magent-frontend",
"private": true, "private": true,
"version": "0803262237", "version": "0.1.0-preview.1",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",
Executable
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env sh
set -eu
compose_file="${MAGENT_COMPOSE_FILE:-compose.yml}"
project_name="${MAGENT_PROJECT_NAME:-magent}"
usage() {
echo "Usage: ./setup.sh check|install|status|token|logs"
echo "Run this script from a directory containing the public compose.yml."
}
compose() {
docker compose -f "$compose_file" -p "$project_name" "$@"
}
case "${1:-}" in
check)
docker version >/dev/null
docker compose version >/dev/null
compose config --quiet
echo "Docker and compose.yml are ready."
;;
install)
compose config --quiet
compose pull
compose up -d --wait --wait-timeout 120
compose ps
echo "Open http://SERVER_IP:3000/setup, then run: ./setup.sh token"
;;
status)
compose ps
;;
token)
compose exec --user magent magent python -m app.container_bootstrap setup-token
;;
logs)
compose logs --tail=150 magent
;;
*)
usage
exit 2
;;
esac