# Ciantic dashboard: first-time setup The website is now part of this bot. It runs on your own laptop. You do not need to buy hosting, a domain, or install Node.js. The bot and website run together. ## 1. Stop and restart your bot In the bot console, press **Ctrl+C**. Double-click **start_bot.bat** again. Keep that console open. Wait for `Logged in as ...` and this line: Ciantic dashboard ready at http://127.0.0.1:8081 Open **http://127.0.0.1:8081** in Chrome or Edge on the same laptop. You should see the Ciantic sign-in page. Sign-in needs the one-time setup below. ## 2. Set up Discord sign-in once 1. Open https://discord.com/developers/applications in your browser. 2. Select the **same application as your Law Enforcement bot**. 3. Open **OAuth2** in the left menu. 4. Under **Redirects**, add this exact URL, then save: http://127.0.0.1:8081/callback 5. Copy the **Client Secret** from the OAuth2 section. This is a separate secret from the bot token. Do not reset or replace your bot token. 6. Open your existing `.env` file yourself, next to `bot.py`, and add a new line: DISCORD_CLIENT_SECRET=YOUR_OAUTH2_CLIENT_SECRET Replace the placeholder with your Client Secret. Keep the existing bot token line unchanged. Do not share either secret or send a screenshot of this file. 7. Save the file, stop the bot with Ctrl+C, and restart **start_bot.bat**. 8. Refresh **http://127.0.0.1:8081** and choose **Sign in with Discord**. 9. Authorise the login using your Discord account. You return to the dashboard. Your Discord account must have the **Web Panel role**, ID `1562912703964651641`. Assign this role in your Discord server before signing in. This is required for non-owner members, including High Command. The owner (`1007571345430806538`) always has full web access while in the server. Set `WEB_PANEL_ROLE_ID` in `config.py` to change the required role. It cannot be changed through the website itself. The Application ID is automatically detected from the bot. If necessary, `DASHBOARD_CLIENT_ID` in `config.py` can hold the Application ID (not a secret). If you change the port, update both the callback in Discord and the address you use. ## 3. Use the dashboard - **Overview:** see bot connection status, recent applications and warnings. - **Applications:** view answers and delivery errors. Configured reviewers can approve, reject with a reason, and retry incomplete delivery. Old fulfilled approvals can be released after their unit role is removed. Decisions use the same database and Discord delivery engine as the bot buttons. - **Inactivity warnings:** configured High Command can search a member, select Warning 1/2/3 and send the existing warning message to the channel and DM. A closed DM is recorded as a delivery issue; the channel post still stands. - **Department tools:** reviewers can refresh the application panel. Only owner account `1007571345430806538` can clean channels, using the existing cleanup account restriction and pin preservation. Deletion is permanent. - **Message builder** (left menu): anyone with the Web Panel role can design messages and choose **Server channel** or **Member direct message**. Channel choices are limited to channels you can view and send in. The server verifies your permissions and the bot's permissions again before sending. For a DM, search by name/ID and select the exact recipient. Closed DMs produce an error. There is a five-second interval between new messages. Repeated requests with the same send reference do not send again, including after a restart. Use **New message** to clear the form and start a separate send. After an uncertain delivery, verify with the member before doing this. - **Settings:** only your owner account can edit the allowlisted channel IDs, role IDs, questions and configurable messages. Discord IDs are quoted strings to preserve their exact value. This first version uses a JSON settings editor. - **Sign out:** ends your website session. Sessions also end after eight hours or a bot restart. Server roles are fetched again on every API request. The owner has full website permissions. Other members use Discord role defaults until the owner saves custom permissions for them in **Panel Access**. Web Panel role holders without reviewer powers can see only their own application records and use the message builder; they cannot see other applicants' answers, warnings, settings or staff actions. Server members without the Web Panel role cannot use dashboard data or actions. Role removal blocks the next request immediately. Applications are still submitted through the Discord buttons. The website is the management panel; it does not add a second application submission flow. Welcome and booster delivery remain automatic. Their text/image options are limited to the settings the existing bot already exposes. The website does not edit the bot token, OAuth secret, server ID or owner account. ### Design and send a message 1. Restart the bot, refresh the website with Ctrl+F5, and open **Message builder**. 2. Choose **Server channel**, then select a channel from the dropdown. Or choose **Member direct message** and select a recipient by name or ID. 3. Write ordinary message content. To add a rich message, tick **Include embed message**. Plain text and embeds can also be sent together. 4. Use the colour picker or hex value. Add a title, description and optional title link. Expand **Author, images, footer and timestamp** for the other options. 5. Add fields and choose inline display if wanted. You can add or duplicate up to ten embeds, with up to 25 fields per embed and 6000 combined embed characters. 6. Add URL buttons if needed. These open a link; they do not run application or review actions. The application's own approval buttons remain separate. 7. Attach up to ten files, totalling at most 8 MiB. To put a local photo inside an embed, upload it and use `attachment://filename.png` as the image URL. Use the sanitised filename shown below the file selector. The files stay in memory while sending and are not stored as website uploads. 8. Check the layout preview and character counters, then click **Send to channel** or **Send DM**. Confirm the displayed destination before sending. The preview shows layout and literal Markdown text; Discord renders Markdown and custom emoji on delivery. Image previews require an accessible HTTP(S) image URL. The bot's name/avatar are used for delivery; author name/icon inside an embed can be customised. No webhook URL is used or required. Mentions do not generate pings. **JSON import/export** accepts a single message's content, embeds and URL-button components (including a single-message Discohook backup wrapper). Unsupported properties are rejected. File bytes are not exported: reattach them when importing an exported design. The editor cannot edit previously sent messages, run custom interactive button actions, create polls, or apply webhook profile/thread settings. Message designs are not automatically saved as drafts; use JSON export to keep one. ## 4. Your records and settings Application and warning records stay in the existing database under `data`. No data import, database reset or manual migration is needed. The dashboard automatically creates a `dashboard_dms` table containing sender, destination ID, timestamp, message ID and delivery status for duplicate protection. Channel sends use a `channel:` reference prefix in this existing metadata table. Message text is neither logged nor stored; a hash is retained to match retries. If delivery is uncertain, check with the recipient before creating another send. Website changes are saved to **data/dashboard_settings.json** and override the corresponding `config.py` settings after restart. Existing application role and channel snapshots are preserved; changing settings does not silently retarget old decisions. For old failed applications requiring route repair, use the existing `/units_retry ... refresh_config:True` in Discord. To return to `config.py` settings, stop the bot, rename `data/dashboard_settings.json` to `dashboard_settings.backup.json`, then restart. Keep the entire `data` folder when moving or backing up the bot. Do not post it publicly: it contains private application answers and warning records. ## Troubleshooting | Problem | What to do | | --- | --- | | Page cannot be reached | Start the updated bot and check for the dashboard-ready line. Open the page on the same laptop. | | Port already in use | Close the other copy of the bot. If needed, change DASHBOARD_PORT and the OAuth callback together. | | Sign-in needs configuration | Add the OAuth Client Secret manually, save and restart. | | Invalid redirect URI | Add exactly http://127.0.0.1:8081/callback to the same bot application's OAuth2 redirects. | | Discord login failed | Check you copied the OAuth Client Secret from the same application as the bot. | | Must be a member of the police server | Log in with the account that has joined the configured Discord server. | | Staff pages or buttons missing | Check the account's reviewer/High Command roles in Discord. | | Settings JSON rejected | Check quotes/commas, unique unit roles, role allowlists, and the question length budget. | | Saved but delivery needs attention | Check the role hierarchy and channel permissions; use Retry after fixing them. | | Page security token expired | Refresh or sign in again after a restart. | ## Access from another computer or phone This version intentionally listens on **127.0.0.1 only**. A phone or another High Command member's laptop cannot open this address to reach your bot. Do not forward port 8081 or change the binding to make it public. A shared, always-online deployment needs HTTPS, a proper public callback URL and secure hosting for the bot and dashboard together. That deployment is not included here. ## Verification Tests use local HTTP requests and temporary databases, with Discord operations mocked. They do not log the bot in or send live warnings/decisions. Real OAuth sign-in requires your one-time Client Secret and redirect configuration above. ## Call Signs Restart the bot with `start_bot.bat`, then refresh the dashboard with Ctrl+F5. The bot posts or refreshes the call sign request panel in channel `1562920280249073715`. High Command can also run `/callsigns_panel`, or use **Call Signs → Refresh request panel** on the website. The blue embed displays `assets/call_signs_request.png` below its text and a 📋 **Request Call Signs** grey button. Members enter their in-game name, then choose **Confirm request**, **Edit name**, or **Cancel**. Only confirmation saves it. The button continues working after restarts. Open **Call Signs → Manage** to see the automatically captured IC name, Discord username and Discord ID. Choose one of the 17 supplied ranks, enter a call sign, and click **Save and update Discord**. Check the nickname in the confirmation before accepting. `103`, `PD103`, `PD-103` and `PD 103` all become `PD 103`; that call sign cannot be reserved by another member, including concurrent saves. The nickname becomes `PD 103 | Harry Wilson`. The complete nickname must fit Discord's 32-character limit. Saving assigns the chosen rank role, removes the member's other roles from `CALL_SIGN_RANKS`, and preserves unrelated roles. The applicant receives a DM containing their rank and call sign. **Send a promotion / rank announcement** is optional; enable it and select a server channel to publish the announcement. Managing requests requires enabled panel access and the **Manage call signs and ranks** permission (included for the owner and reviewer role defaults). Other panel users see only their own request. The bot needs **Manage Roles** and **Manage Nicknames**; place its role above the member's highest role and every rank it will manage. Discord does not allow the bot to rename the server owner. The request channel also needs View Channel, Send Messages, Embed Links, Read Message History and Attach Files for the bot. Partial failures appear as **NeedsAttention** with details. Correct permissions, role hierarchy or closed DMs, then click **Retry incomplete actions**. The bot also retries unfinished work every two minutes; successful role, nickname and message actions are not deliberately repeated. A failed DM does not undo the rank or nickname assignment. Discord delivery and a local database write are not one transaction, so a crash immediately after sending a message can cause a duplicate on recovery. Settings and all supplied role IDs live in `config.py`: `CALL_SIGN_RANKS`, `CALL_SIGN_REQUEST_CHANNEL_ID`, and `CALL_SIGN_REQUEST_IMAGE_PATH`. Requests, exclusive call sign reservations, delivery progress and change history use new tables in the existing SQLite database. Tables are created automatically: no manual migration or extra dependencies are needed. Keep the existing `data` folder when restarting or moving the bot. Manual Discord rank/nickname changes are not automatically imported into the website; make subsequent changes here. ### Saving and notifications **Save and update Discord** opens a confirmation inside the website. Check the nickname and rank, then click **Confirm and update Discord**. **Go back** keeps your edits without submitting. While saving, the button shows progress and is disabled. Required field errors and Discord permission failures appear inside the form. Notifications remain as a dismissible banner inside the page. If saving fails, read the message in the form: call signs already reserved by another member cannot be used; the full nickname must fit 32 characters; and the bot must have Manage Roles, Manage Nicknames and sufficient role hierarchy. After a code update restart `start_bot.bat` and press Ctrl+F5 in the website. ### Pending, assigned and staff actions **Call Signs** now has separate **Pending requests** and **Assigned call signs** lists with counts. Unfinished or failed assignments appear in **Needs attention** until their remaining actions succeed. All lists refresh every 15 seconds. Processed records show **Assigned / updated by** (staff name and Discord ID) and **Action date**. Open Manage/View to see the same details. The name is saved with the assignment so it survives restarts and later Discord name changes. Older records without a saved staff name show the current Discord display name when available, resolved from the stored staff ID. This lookup does not change the historical audit. Unavailable accounts retain their ID with Name unavailable. Later edits show the latest staff action; earlier revisions remain in the SQLite audit history. The new reviewer-name column is added automatically without deleting records. ## Panel Access Open **Panel Access** in the sidebar to see the server display name, Discord username and exact Discord ID of every human server member who currently has the configured Web Panel role. Click **Refresh** for the latest list. The bot fetches the full member roster, including members missing from its local cache. Server Members Intent must be enabled. Bots are excluded because they cannot sign into the website through user OAuth. The access directory is visible to the owner and members with **View panel access and permissions**. It lists eligibility to sign in, not active sessions. Only the owner can edit feature permissions or disable access. Non-owner members still need the Web Panel role in Discord. ## Separate member permissions Discord ID **1007571345430806538** has protected full website access. This owner can use every feature and can sign in without the Web Panel role, but must still be a member of the configured server. No other account can become the owner or edit permissions through a checkbox or forged API request. 1. Restart the bot and refresh the website with Ctrl+F5. 2. Sign in with the owner account and open **Panel Access**. 3. Find the member by name and Discord ID. New members must first receive the Web Panel role (`1562912703964651641`) in Discord. 4. Click **Edit permissions**. Keep **Allow this member to use the panel** on, then select the features they may use. 5. Click **Save permissions** and confirm. To block all website access, turn off the access checkbox and save. The owner row is protected from edits. Feature permissions are independent: | Permission | Allows | |---|---| | Review vehicle applications | View all applications, approve/reject, retry, release | | Manage call signs and ranks | View all call sign requests, assign/update ranks and call signs, retry | | Issue and view inactivity warnings | List, issue and retry warnings | | Send member DMs | Use the message builder for DMs | | Send server channel messages | Use the message builder for server channels | | Refresh Discord request panels | Refresh vehicle-unit and call-sign panels | | View panel access and permissions | Read the access directory and effective permissions | Permission editing, bot settings and chat cleanup remain owner-only. Granting access-directory viewing does not allow permission editing. Members with no feature permissions can still view their own application and call sign status. Existing members keep their previous Discord role defaults until you save custom choices for them. Custom choices then override those defaults, including High Command roles. Disabled access and removed Web Panel roles block existing sessions on their next API request. Refresh/reload updates the visible controls. Web permissions do not grant Discord slash command permissions or change roles. Channel sends still respect the member's Discord channel permissions; the bot still needs its normal channel permissions and sufficient role hierarchy. Permissions and an owner/action/timestamp audit are saved in two new tables in the existing SQLite database. They survive restarts. Tables are added automatically; no manual migration or new dependency is required. ## Bot status, files and console Restart `start_bot.bat` and refresh the website with Ctrl+F5. No extra package, website process or port is needed. The overview now shows connection status, uptime/start time, latency, bot identity, runtime versions, application/call-sign counts and (for console users) queued-log count. Counts respect the viewer's application/call-sign permissions. Overview refreshes every 15 seconds. Members who cannot access the panel see: **You don't have access to this panel. Contact a staff member.** Settings remains owner-only on both the website and API. The owner can set `BOT_STATUS` to `online`, `idle` or `dnd` and change `BOT_ACTIVITY_TEXT` in Settings. This updates the bot's Discord presence; the connection indicator separately reports whether Discord is connected. ### File manager The owner has **Project files** by default. In **Panel Access → Edit permissions**, the owner may grant **Manage bot and website files (trusted administrator)**. Use the folder list to open/download current files, edit UTF-8 text, create a text file or upload supported source/assets. Existing-file replacements use a SHA-256 revision check and confirmation to avoid overwriting another edit. Python/JSON syntax is checked before atomic replacement. Text editing is limited to 1 MiB; individual uploads/downloads to 8 MiB. Single-file uploads use existing folders; ZIP imports may create safe subfolders. Credential files, hidden paths, instruction files, private `data` and log/database folders, links/junctions and paths outside the project are excluded. The file manager cannot delete files or run terminal commands. Use Console's export for logs; the live database is not uploaded/replaced here. **File management is a powerful administrator permission:** editing executable source can change the bot's behaviour and permission system. Give it only to people trusted to administer the bot. Restart the bot after Python/configuration source changes; refresh the browser after website source changes. The running bot is not automatically restarted or hot-reloaded by saving a file. ### Console and Discord log embeds The owner has **Bot console** by default; **View bot console and logs** can be granted separately. The console displays persistent INFO, warning, error and critical Python logs plus action audits. It refreshes every five seconds. Filter by severity/text, load older history, or reload to return to live events. The viewer shows up to 1,000 rows at once; older retained events remain pageable. **Download latest 1,000 logs** exports the matching latest window. `BOT_LOG_CHANNEL_ID` is configured as **1563122334037049374** (`pd-bot-log`). Give the bot View Channel, Send Messages and Embed Links there. Configure that Discord channel's viewers for the staff who should see audit logs; web console permissions do not control Discord channel visibility. Captured events include startup/reconnect/shutdown requests, commands and interaction IDs, member joins/leaves and rank/nickname changes, privileged website actions, file operations and existing bot warnings/errors. Submitted application answers, DM/message bodies and rejection reasons are not audited. Known runtime secrets and token/password/auth patterns are redacted before storage or forwarding. Ordinary `print()` output is not captured; current bot modules use Python logging. Capture starts with the updated bot, so earlier unsaved terminal output cannot be reconstructed. Normal bot logs/audits are queued persistently and sent as professional embeds with source, severity, timestamp and reference ID. Long entries are split into chunks; saved progress resumes after restarts. Delivery failures back off and retry, and backlog is visible in Overview/Console. Transport diagnostics caused by forwarding itself remain console-only to prevent a logging loop. Entries over 32,768 characters have an explicit truncation marker. No history is silently pruned; keep the data folder backed up and allow space for logs to grow. A crash immediately after Discord accepts a batch but before SQLite records it can cause that batch to be delivered again on recovery. All log/permission tables are added automatically in the existing SQLite file; no manual migration or dependency changes are required. A console is a read-only log viewer, not an interactive terminal. ## Black panel, bulk files and PD Members Restart `start_bot.bat`, then press **Ctrl+F5** to load the black iOS-inspired interface with matching outline icons. ### Download or upload the project In **Project files**, **Back** returns to the previously visited folder, **Up** opens its parent, and **Project home** opens the project root. **Download all** creates one ZIP of supported bot/website source and assets. Secrets, credentials, private databases/logs, hidden/instruction files, links and unsupported files are excluded. This is not a complete data backup: back up the live database separately while the bot is stopped. Edit downloaded files and ZIP the project contents with paths such as `dashboard_static/style.css` at the ZIP root. Choose **Upload all**, select the ZIP, inspect the preview, then confirm. Files absent from the ZIP are kept. Limits: 32 MiB compressed, 64 MiB uncompressed, 1,000 supported files; individual size/type rules still apply. Protected or unsafe paths reject the import. Previews expire after five minutes and belong to the signed-in session. The server rechecks access, CSRF, archive identity, syntax and current file hashes. Changes are staged and replaced atomically per file; ordinary write failures trigger rollback. This is not a crash-atomic deployment: keep a backup before large changes. If rollback fails, recovery originals remain in a protected `.panel-bulk-*` folder for local administrator recovery. Restart after Python/ config changes; refresh after website changes. File management is owner-only by default and separately delegable. Editing executable bot source effectively grants bot administrator power; give this permission only to trusted administrators. ### PD member directory **PD Members** lists human members holding a configured department or police rank role. `PD_MEMBER_ROLE_IDS` in config.py controls department roles. IC names and call signs use saved requests when available, otherwise the member's `Call sign | Name` nickname. Rank comes from the highest configured rank role currently held in Discord. Discord usernames and IDs fill automatically. The bot needs Server Members Intent to fetch the directory. Select **View profile** for existing warning history and private staff notes. Notes record author and date. Deleting a note hides it while retaining an audit record. **Delete profile and retire** removes all assignable Discord roles, adds the configured Retired Officer role, then moves the profile to **Deleted profiles**. Managed roles and @everyone remain. It does not kick the member or erase warning/ application history. Retirement does not automatically release call signs. In **Panel Access**, the owner can separately grant: - **View PD members, warnings and staff notes** (`members`). - **Add and delete PD staff notes** (`member_notes`). - **Manage deleted PD profile records** (`member_delete`). - **Retire PD members and remove all assignable Discord roles** (`member_retire`). Note/delete grants require directory viewing. These permissions are owner-only by default; existing custom grants do not gain them automatically. Every API request checks access again. Revisions block stale changes, and the server records the signed-in actor. Settings and permission editing remain owner-only. New profile, note and audit tables are created automatically in the existing SQLite database; no manual migration or package installation is needed. ## Police member profiles, leave and resignations Restart the bot with `start_bot.bat` and refresh the browser with **Ctrl+F5**. No new package or manual migration is required: personnel request, notification and audit tables are added to the existing SQLite database automatically. ### Member access A current human member with a configured `PD_MEMBER_ROLE_IDS` department role or one of `CALL_SIGN_RANKS` can sign in and open **My Profile** without the staff Web Panel role. This is a limited self-service account: it cannot open staff routes, search other members, read staff notes or edit department records. An owner-set access block still prevents login. Staff tools continue to require the Web Panel role and their individual permissions; the owner retains full access. My Profile shows Discord avatar/username/ID, IC name, call sign, live rank, department status, the member's own inactivity warnings, request history and private website notifications. A former officer whose resignation completed can still view their own history while they remain in this Discord server, unless access is explicitly blocked. They cannot submit new requests without a police role. This does not create a rehiring workflow; staff must handle a returning former officer's personnel status before resuming automated assignments. All requests are **website only**. There is no leave/resignation Discord form, panel or command. Discord is used for decision announcements, DMs and role changes. ### Submit and track requests - **Request Leave of Absence:** choose first day of leave, return-to-duty date and a private reason. Leave ends at the start of the return date, using UTC. For example, 24 October to return on 27 October covers the 24th, 25th and 26th. Maximum duration is `MAX_LEAVE_DAYS` (90 by default). - **Submit Resignation:** choose the final working date and private reason. That date is included: approved role changes run after its UTC midnight ends. - Review the complete details in the website confirmation before submitting. Cancel a request only while it is pending. Rejected/cancelled requests remain in history and do not prevent another request. Only one pending request of each type is allowed. Approved leave periods cannot overlap, and leave cannot extend past a scheduled resignation. The database checks submissions and reviews transactionally, so concurrent requests or reviews cannot create conflicting decisions. Staff cannot review their own request. Expired dates cannot be approved. ### Staff review and channels **Leave Requests** and **Resignations** are separate sidebar sections. High Command/reviewer role defaults include both, provided the member has the Web Panel role. Existing saved custom grants remain unchanged: the owner must enable new permissions explicitly for those members. In **Panel Access**, independently grant **Review leave of absence requests** (`leave_review`) and **Review resignation requests** (`resignation_review`). Viewing/reviewing requests uses the same permission for its section; ordinary police members only see their own. Open a request to approve it or reject it with a required reason. Decisions show the actual reviewer name/ID and time. Resignation approval has an extra website confirmation listing role IDs and the nickname/call sign policy. Decisions are recorded before notifications; failed announcements or DMs do not undo them. | Setting | Decision announcement channel | | --- | --- | | `LEAVE_RESPONSE_CHANNEL_ID` | `1266839309575520285` | | `RESIGNATION_RESPONSE_CHANNEL_ID` | `1266839309575520282` | Approved **and** rejected decisions send a professional embed to the matching channel. Embeds include the member, IC name/call sign, decision, dates, reviewer and timestamp. Personal request/rejection reasons stay in the authorised website record and applicant DM; they are not published in those channels or audit logs. Give the bot View Channel, Send Messages and Embed Links in both channels. Pending requests are reviewed on the website, without a separate review channel. ### Scheduled changes and settings `LEAVE_ROLE_ID = 0` means tracking without an extra Discord role. Set it to an On Leave role ID in config.py or owner Settings if you want automatic assignment. Place the bot above that role and enable Manage Roles. The bot removes the leave role on return only when this request added it; an already-held role is retained. `RESIGNATION_REMOVE_ROLE_IDS` is the explicit removal allowlist. Its defaults contain configured department, rank, unit, applicant, High Command/reviewer and Web Panel roles. Review this list in Settings before approving a resignation. Other Discord roles are retained; nobody is kicked or banned. The PD profile is archived on completion. `RESIGNATION_RESET_NICKNAME` and `RESIGNATION_RELEASE_CALL_SIGN` default to **false**; the owner may enable them. Release frees the call sign but retains request/change history. The approved role/nickname/call-sign policy is saved with the request so later setting changes do not silently change scheduled actions. The scheduler checks about every 60 seconds while the bot is connected. Leave moves through Scheduled, On Leave and Completed; resignation moves through Scheduled and Resigned. Offline work catches up after restart, without briefly adding a leave role for an already-ended interval. Keep the bot running for on-time updates. Use only one running bot process per database. Failures remain visible as **Action required** or delivery notices. Staff can use **Retry incomplete actions** after correcting Discord permissions/membership. Announcement and scheduled-action failures are also retried by the scheduler; failed DMs require an explicit staff retry to avoid repeatedly messaging a closed inbox. Website notifications remain available. As with other bot delivery, a crash just after Discord accepts a message but before its receipt is saved can cause a duplicate message on recovery. Role operations are idempotent. PD Members displays the leave/resignation status. Website inactivity warnings look up approved leave and require an explicit warning override confirmation. The existing Discord warning commands retain their current behaviour. The website is still bound to `127.0.0.1:8081` on your laptop. That address is local to each device; other members need a separately hosted, HTTPS-enabled version to use it remotely. This change does not expose the laptop to the internet. ## Ciantic Night appearance and profile retirement The shared theme is now black and charcoal with restrained purple accents, matching outline icons, grouped cards, form controls, dialogs and status badges. The supplied department photos are local, optimised JPG backgrounds for the login screen, overview banner and member profile. No remote image service or font dependency is required. A small police badge appears in the browser tab, header and navigation. Restart the bot for backend changes and press **Ctrl+F5** to reload the new theme; a browser tab may need reopening to refresh its favicon. **Delete profile and retire** is now a Discord role-changing action. It requires both the existing `member_delete` permission and the NEW `member_retire` grant, plus member-directory viewing. Existing custom grants do not receive the new retirement power automatically. The owner can grant it under Panel Access. Delegated staff may only retire a member below their own highest Discord role; self-retirement, panel-owner and Discord-server-owner targets are blocked. The bot adds `RETIRED_OFFICER_ROLE_ID = 1284004849502912553` and removes **all assignable roles**, including non-police and staff roles, before archiving the profile. Discord-managed/integration roles and @everyone cannot be removed and are listed as retained. Put the bot's role above the target's highest role and Retired Officer, and grant Manage Roles. The website asks for explicit confirmation. There is no automatic kick or ban. Role changes are persisted as a retirement job with the initiating staff member, time, original role list, retained roles, outcome and errors. If any change fails, the profile is not reported as fully retired: refresh PD Members and use **Retry retirement** after fixing permissions. Incomplete jobs remain in the directory even if police roles were already removed. The replacement role is added first so partial removal cannot leave the member without retired status. Retry checks live roles and does not add duplicates. The server verifies the final roles before completing the archive. A crash after the archive can be resolved through the same retry action. Retired members have only their own profile/history access, subject to owner access blocks. Older call-sign/unit delivery jobs cannot restore department roles, and scheduled personnel role changes pause for staff review. Archived records retain notes, warning/application/call-sign history and retirement audit. Retirement is not undone by Restore profile; any rehire needs explicit owner handling of the retirement record and roles. Legacy archive-only profiles can still restore their directory record without restoring Discord roles. New SQLite retirement tables are created automatically; no manual migration or new dependency is required. Changes to the appearance do not change other feature permissions. ## Profile landing page and Department Manual After signing in, current police members and the owner must first read and acknowledge the Department Manual if they have no saved acknowledgement. After that, everyone who can use the website lands on **My Profile**, including staff. The profile hides the staff sidebar. Members with current panel access see **Open Control Panel**; the panel hides profile content and has **Back to My Profile** beside the account controls. Opening the panel refreshes server permissions. Existing API permission checks remain enforced. Staff without a police role can read their own basic profile but cannot submit police requests or read the Department Manual. Retired members keep their own history only, subject to owner access blocks. The profile shows a **Read Department Manual** button, rather than the manual content itself. It opens a separate reader with searchable, expandable chapters for the five supplied department rules, the roster from level 16 down to 0, all supplied radio codes, and the arrest guide. Reference content lives in `services/department_manual.py` and is fetched from the protected `/api/profile/manual` endpoint. It is absent from public static files. Code 10 says "Description not supplied". PD Count List, Situation Loadout, Task To Rank Up and Pullover Guide await supplied instructions. Restart with `start_bot.bat`, open http://127.0.0.1:8081 and press **Ctrl+F5**. No new IDs or dependencies are needed. The existing SQLite database gains a `department_manual_reads` table automatically on restart. It saves member/guild, manual version and acknowledgement time. Confirmations are idempotent and cannot be submitted for another member. A failed save leaves the reader open for retry. Existing members must complete this step on their next login after the update. ## Latest update: promotion tasks, Pullover Guide and access See FEATURE_UPDATE.md for current permissions and instructions. Current department role membership is required for website access (owner exempt). Rank-only, panel-only and retired/former-member access is no longer allowed. The panel now has Promotion Tasks for authorised staff, and members have their own task progress card. Task To Rank Up uses the same saved requirements. Pullover Guide now has seven simple steps and four protected 4K training photos. Restart start_bot.bat and refresh with Ctrl+F5; manual version 2 needs a new one-time acknowledgement. New SQLite tables are created automatically. ## Owner database manager Restart the bot, refresh the browser with Ctrl+F5, then open Control Panel > Database. Only the configured DASHBOARD_OWNER_ID (currently 1007571345430806538) can use this section. Database access cannot be delegated through Panel Access. - Choose a table. Search by Discord ID, name or another stored value. Results are paginated at 50 records per page. - Use View / edit to inspect a complete record. Editable values use JSON: keep field names and primary keys unchanged, preserve quotes around Discord IDs, and use null for an empty database value. - Review changes shows each edited value before confirmation. Delete record asks for confirmation. An outdated record must be reloaded before saving. - Download database backup exports a consistent SQLite copy. This includes private department records; keep it private. - Automatic backups are saved under data/database_backups before record changes. Backups persist with the existing Docker data volume. Backup retention is manual. - Audit/log tables and tables without primary keys are read-only. There is no raw SQL console, schema editor, table wipe, or live database upload/restore action. To remove a warning: select inactivity_warnings, search the member's Discord ID, open the matching warning, and choose Delete record. Refresh the member's profile; the warning and its count will update. Existing Discord announcement messages and DMs are not removed. Generic record edits do not trigger Discord delivery, rank roles, nickname changes, or application workflows; use the normal panel actions for those operations. Editing permission records can change account access. To restore an automatic backup, stop the bot first, preserve the current database, and replace data/vehicle_units.sqlite3 with the chosen backup. Do not restore while any bot process is running. No migration or new dependency is required.