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
18 changes: 9 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
framework-only Clanktank source (single `cogs/clanktank.py`, the slim clanker
schema and `bot_manifest.py`) is replaced by the self-contained server-tools
app (its own `clanklib/`, `api/v2`, slim server-only `database/` and cogs).
Identity is rebranded to **Recycler** throughout (`sojourns.json` slug/name,
app name, webhook + backup labels), so the bot deploys via Sojourns under its
Identity is rebranded to **Recycler** throughout (`auren.json` slug/name,
app name, webhook + backup labels), so the bot deploys via Auren under its
own `recycler` slug just like before. The `.clank` containment + moderation
suite now lives in the separate **Clanksimus Prime** bot.

Expand All @@ -29,10 +29,10 @@

### New Features
- **Per-guild settings API**: A key-protected REST surface (`/api/v2/guilds/{id}/settings`, with a `/schema` endpoint) to read and update each server's config (prefix, log + containment channels) from outside Discord, sharing one validated schema with the `.set` command.
- **Live settings**: Configuration changes made in the Sojourns control panel
- **Live settings**: Configuration changes made in the Auren control panel
now take effect immediately, without a redeploy. Cogs read runtime config
(prefix, backup cap, API key, client id) from the live settings layer rather
than a boot-time environment snapshot, so the Sojourns control link's
than a boot-time environment snapshot, so the Auren control link's
per-heartbeat sync actually reaches the commands.
- **Server backups**: Create full, restorable guild snapshots (settings, roles,
categories, channels, permission overwrites and optional recent messages),
Expand All @@ -54,7 +54,7 @@

### Documentation
- Thick, end-to-end deployment guide (`docs/deployment.md`) covering all four
pathways - local/bare-metal, Docker, Railway and Sojourns - with
pathways - local/bare-metal, Docker, Railway and Auren - with
prerequisites, post-deploy verification, upgrade/rollback and troubleshooting.
- Installation quick-start, full configuration reference and a complete command
reference.
Expand Down Expand Up @@ -121,7 +121,7 @@
`.modlog prune <days>` and `.modlog test`. Muted categories are still
recorded for the timeline.
- Per-category log routing and a default mod log channel, configurable in both
Discord and the Sojourns web UI.
Discord and the Auren web UI.
- **Tamper-evident audit chain**: every event stores the hash of the previous
event plus its own; `.modlog verify` walks the chain and reports the first
break (an altered or deleted row).
Expand All @@ -137,22 +137,22 @@

### Changes
- Components V2 is the default UI across every command.
- Templated for Sojourns via `sojourns.json` (validated in CI).
- Templated for Auren via `auren.json` (validated in CI).
- **Role-based clanker hunters**: The scam-hunter system now keys off a
configurable hunter role instead of a per-user whitelist. Anyone wearing the
role can report in the hunter channel and is immune to automatic clanking;
the old `.clank hunter add/remove` user commands are replaced by
`.clank hunter role @role`.
- **Full config surface, Discord and web**: Every containment/moderation option
is now editable both in Discord (`.set ...`) and in the Sojourns web UI, and
is now editable both in Discord (`.set ...`) and in the Auren web UI, and
the two surfaces write the same per-guild keys. New/exposed options: clanker
role, clanker category, clanktank channel, clanker log channel, escape-room
thread, reflection period, hunter role, hunter channel, mod log channel.
- **Reflection period default is now 5 minutes** (was 8), and is configurable
per server (`.set reflection <minutes>` or the web UI).

### Fixes
- **Web-UI settings now actually apply**: settings pushed from the Sojourns
- **Web-UI settings now actually apply**: settings pushed from the Auren
control plane used manifest env-style keys (`CLANK_ESCAPE_THREAD_ID`) while
the bot read canonical lowercase keys (`clank_escape_thread`); the two
namespaces never met, so values set in the web UI silently no-op'd. The DB
Expand Down
12 changes: 6 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

Recycler is a free Discord server-management bot (backups, templates,
chatlogs, sync, import/export, settings), built on the shared **bot framework**
(`hilleywyn/framework`) and templated for the **Sojourns** platform via
`sojourns.json`.
(`hilleywyn/framework`) and templated for the **Auren** platform via
`auren.json`.

## Default UI -- Components V2 (hard rule)

Expand All @@ -34,11 +34,11 @@ feature genuinely requires one (there are currently none). This needs

## Architecture

- `main.py` -- three lines: `run_manifest()` boots from `sojourns.json`
- `main.py` -- three lines: `run_manifest()` boots from `auren.json`
(its `features` is the cog list) with a fallback cog list.
- `sojourns.json` -- the manifest. Source of truth for cogs + settings; the
Sojourns control plane reads the same file. Validate with
`python -m core.framework.manifest sojourns.json`.
- `auren.json` -- the manifest. Source of truth for cogs + settings; the
Auren control plane reads the same file. Validate with
`python -m core.framework.manifest auren.json`.
- `cogs/` -- the Components V2 server tools: `backups.py`, `templates.py`,
`chatlog.py`, `sync.py`, `importexport.py`, plus `settings.py` and `meta.py`.
- `clanklib/serializer.py` -- guild <-> JSON (the engine behind backups +
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ A free, modern Discord **server-management bot** -- backups, templates,
chatlogs, sync, import/export and settings -- rendered in Discord's
**Components V2** UI, with **no premium tiers and no paywalls**. Built on the
shared [`bot framework`](https://github.com/hilleywyn/framework) and templated
for the [Sojourns](https://github.com/hilleywyn/sojourns) platform.
for the [Auren](https://github.com/hilleywyn/auren) platform.

> Inspired by what Xenon does for server management -- rebuilt on a modern,
> open stack with the premium gating removed.
Expand Down Expand Up @@ -48,7 +48,7 @@ docker run --env-file .env -p 8080:8080 recycler

- **[`docs/deployment.md`](docs/deployment.md)** -- the thick, end-to-end
deployment guide: prerequisites and all four pathways (local/bare-metal,
Docker, Railway, Sojourns), post-deploy verification, upgrades/rollback and
Docker, Railway, Auren), post-deploy verification, upgrades/rollback and
troubleshooting. **Start here.**
- [`docs/configuration.md`](docs/configuration.md) -- every environment
variable, grouped by feature, with defaults and effects.
Expand All @@ -57,9 +57,9 @@ docker run --env-file .env -p 8080:8080 recycler

## How it's built

`main.py` boots from `sojourns.json` through the framework's shared runtime
`main.py` boots from `auren.json` through the framework's shared runtime
(`run_manifest`). The manifest's `features` list is the set of cogs to load and
doubles as the deployment contract the Sojourns control plane reads. The data
doubles as the deployment contract the Auren control plane reads. The data
plane is a slim, economy-free Postgres layer with a file-based migration runner.
The UI is Components V2 throughout (`core.framework.components`).

Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions clanklib/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
Precedence, highest first:

1. Per-guild override stored in ``guild_settings`` (set via ``.set ...``).
2. The bot's live ``Settings`` object (``bot.settings``), which the Sojourns
2. The bot's live ``Settings`` object (``bot.settings``), which the Auren
control link refreshes on every heartbeat -- so a change made in the
Sojourns UI takes effect without a redeploy.
Auren UI takes effect without a redeploy.
3. The matching environment variable.
4. The caller's default.

Expand Down
2 changes: 1 addition & 1 deletion cogs/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This build (server tools: backups/templates/chatlog/sync/import-export) exposes
just the prefix and the log channel. The same per-guild keys are editable from
the Sojourns web UI via clanklib.guild_schema, so the two surfaces agree.
the Auren web UI via clanklib.guild_schema, so the two surfaces agree.
"""
from __future__ import annotations

Expand Down
6 changes: 3 additions & 3 deletions database/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ async def update_guild_setting(self, guild_id: int, key: str, value: Any) -> Non
"""Set a real column (when ``key`` is one) or a ``features`` JSONB key.

``key`` is normalised through :data:`_GUILD_KEY_ALIASES` first so that
a setting pushed from the Sojourns control plane (manifest env-style
a setting pushed from the Auren control plane (manifest env-style
keys like ``CLANK_ESCAPE_THREAD_ID``) lands under the same canonical
lowercase key the cogs read (``clank_escape_thread``). Without this the
web UI and the bot use divergent namespaces and settings silently no-op.
Expand Down Expand Up @@ -239,10 +239,10 @@ def sync(self): # type: ignore[no-untyped-def]
})


# Map the manifest's env-style setting keys (what the Sojourns web UI and the
# Map the manifest's env-style setting keys (what the Auren web UI and the
# control-plane heartbeat speak) onto the canonical lowercase per-guild keys the
# cogs read. Any unmapped key passes through unchanged. This is the single
# bridge between the two namespaces; keep it aligned with sojourns.json and
# bridge between the two namespaces; keep it aligned with auren.json and
# clanklib.guild_schema.GUILD_FIELDS.
_GUILD_KEY_ALIASES: dict[str, str] = {
"PREFIX": "prefix",
Expand Down
4 changes: 2 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Configuration

Every setting is an environment variable (locally via `.env`, on Railway via
service variables, on Sojourns via the settings UI generated from
`sojourns.json`). Defaults are shown where they exist.
service variables, on Auren via the settings UI generated from
`auren.json`). Defaults are shown where they exist.

## Required

Expand Down
30 changes: 15 additions & 15 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ covers all four pathways:
2. [Pathway A - Local / bare metal](#pathway-a--local--bare-metal)
3. [Pathway B - Docker](#pathway-b--docker)
4. [Pathway C - Railway](#pathway-c--railway)
5. [Pathway D - Sojourns (managed)](#pathway-d--sojourns-managed)
5. [Pathway D - Auren (managed)](#pathway-d--auren-managed)
6. [Post-deploy verification](#6-post-deploy-verification)
7. [Upgrades, backups of the bot, and rollback](#7-upgrades-and-rollback)
8. [Troubleshooting](#8-troubleshooting)
Expand Down Expand Up @@ -286,46 +286,46 @@ build variable you might set selects the ref:

---

## Pathway D - Sojourns (managed)
## Pathway D - Auren (managed)

Best when you want the Sojourns control plane to deploy, configure and manage
the bot. Recycler ships a `sojourns.json` manifest, which is the deployment
contract Sojourns reads.
Best when you want the Auren control plane to deploy, configure and manage
the bot. Recycler ships a `auren.json` manifest, which is the deployment
contract Auren reads.

### D.1 What the manifest declares

`sojourns.json` tells Sojourns:
`auren.json` tells Auren:

- **identity** - slug, name, version, repo;
- **`features`** - the exact cog list to load (same list `main.py` boots from);
- **`credentials`** - the secrets to collect (`DISCORD_TOKEN`, marked secret);
- **`provision`** - that it needs a Postgres database;
- **`settings`** - grouped fields (prefix, API port, client id, backup cap, API
key) that Sojourns renders as a dynamic configuration UI and pushes to the
key) that Auren renders as a dynamic configuration UI and pushes to the
running bot via `bot.settings`.

Validate it any time:

```bash
python -m core.framework.manifest sojourns.json
python -m core.framework.manifest auren.json
```

### D.2 Register and deploy

1. In Sojourns, add a **managed bot** pointing at the `recycler` repo.
Sojourns parses `sojourns.json` and shows the identity, the feature list, the
1. In Auren, add a **managed bot** pointing at the `recycler` repo.
Auren parses `auren.json` and shows the identity, the feature list, the
credentials to collect and the settings UI.
2. When prompted, paste `DISCORD_TOKEN`. Sojourns stores it in its vault (the
2. When prompted, paste `DISCORD_TOKEN`. Auren stores it in its vault (the
field is declared `secret`), provisions Postgres
(`provision.database = "postgres"`) and injects `DATABASE_URL`.
3. Set any settings in the generated UI - prefix, `BACKUP_MAX_PER_USER` and
`CLANK_API_KEY`. Each maps to a control defined in the manifest and is
delivered to the bot without a code change.
4. Deploy. Sojourns runs the same Dockerfile/runtime; first boot applies
4. Deploy. Auren runs the same Dockerfile/runtime; first boot applies
migrations.

Because `main.py` boots from the **same** `sojourns.json`, there is no drift
between a standalone run and a Sojourns-managed deployment - the manifest is the
Because `main.py` boots from the **same** `auren.json`, there is no drift
between a standalone run and a Auren-managed deployment - the manifest is the
single source of truth for both.

---
Expand All @@ -351,7 +351,7 @@ Run these regardless of pathway:

## 7. Upgrades and rollback

- **Code upgrade**: redeploy the branch/tag. On Railway/Sojourns this is a
- **Code upgrade**: redeploy the branch/tag. On Railway/Auren this is a
push or a redeploy click; with Docker, rebuild and `docker compose up -d`.
New migrations apply automatically on boot; existing data is preserved.
- **Framework upgrade**: bump `FRAMEWORK_REF` to the desired ref and rebuild.
Expand Down
18 changes: 9 additions & 9 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ from git.
- [2. Local development](#2-local-development)
- [3. Docker](#3-docker)
- [4. Railway](#4-railway)
- [5. Sojourns](#5-sojourns)
- [5. Auren](#5-auren)

---

Expand Down Expand Up @@ -53,7 +53,7 @@ docker run -d --name recycler-db -e POSTGRES_PASSWORD=clank \
Validate the manifest any time with:

```bash
python -m core.framework.manifest sojourns.json
python -m core.framework.manifest auren.json
```

## 3. Docker
Expand Down Expand Up @@ -86,20 +86,20 @@ the container healthcheck.
ref to install (branch/tag/commit; defaults to `main`).
5. Deploy. The healthcheck path is `/health`; first boot runs migrations.

## 5. Sojourns
## 5. Auren

Recycler ships a `sojourns.json` manifest, so the Sojourns control plane can
Recycler ships a `auren.json` manifest, so the Auren control plane can
deploy and manage it like any other managed bot:

1. In Sojourns, add a managed bot pointing at this repo. Sojourns reads
`sojourns.json` for the bot identity, the `features` (cogs) to load, the
1. In Auren, add a managed bot pointing at this repo. Auren reads
`auren.json` for the bot identity, the `features` (cogs) to load, the
`credentials` it must collect (`DISCORD_TOKEN`) and the `settings` to render
as a dynamic config UI.
2. Provide `DISCORD_TOKEN` when prompted; Sojourns provisions Postgres
2. Provide `DISCORD_TOKEN` when prompted; Auren provisions Postgres
(`provision.database = "postgres"`) and injects `DATABASE_URL`.
3. Adjust settings (prefix, backup cap, API key) from the Sojourns settings UI
3. Adjust settings (prefix, backup cap, API key) from the Auren settings UI
-- each field in the manifest maps to a control and is pushed to the running
bot via `bot.settings`.

The same manifest is what `main.py` boots from locally, so there is no drift
between a standalone run and a Sojourns-managed deployment.
between a standalone run and a Auren-managed deployment.
4 changes: 2 additions & 2 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Recycler entrypoint.

Boots from ``sojourns.json`` (the manifest's ``features`` is the cog list and
Boots from ``auren.json`` (the manifest's ``features`` is the cog list and
``bot.name`` is the app name) via the shared framework runtime. If the
manifest is missing or invalid the bot still starts from the fallback cog
list below, so a packaging slip can never make a working bot refuse to boot.
Expand All @@ -22,7 +22,7 @@

from core.framework.run import run_manifest # noqa: E402 (must follow the JWT_SECRET default above)

# Mirrors sojourns.json -> features. Kept in sync as a safety net only; the
# Mirrors auren.json -> features. Kept in sync as a safety net only; the
# manifest is the source of truth at runtime.
_FALLBACK_COGS = [
"cogs.meta",
Expand Down
4 changes: 2 additions & 2 deletions tests/test_manifest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Validate sojourns.json without importing the (discord-dependent) framework.
"""Validate auren.json without importing the (discord-dependent) framework.

These checks mirror the framework's manifest contract and additionally assert
that every declared feature maps to a real cog module in this repo, so a typo
Expand All @@ -10,7 +10,7 @@
import pathlib

ROOT = pathlib.Path(__file__).resolve().parent.parent
MANIFEST = ROOT / "sojourns.json"
MANIFEST = ROOT / "auren.json"

ALLOWED_TYPES = {
"string", "number", "boolean", "select", "secret",
Expand Down
2 changes: 1 addition & 1 deletion tests/test_settings.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Tests for clanklib.settings -- the live-config resolution layer.

The point of this module is that config changes pushed from the Sojourns UI
The point of this module is that config changes pushed from the Auren UI
(into ``bot.settings``) take effect without a redeploy, with env and defaults
as fallbacks. These tests pin that precedence with a stub bot.
"""
Expand Down
Loading