This changelog highlights user-facing improvements across the Tools platform.
/feed/category-content rendering no longer crashes when cached article/category text contains local development URLs such as http://127.0.0.1/... or http://localhost/.... Host-reference cleanup in the public URL normalizer now uses a corrected pattern again..net/alias hosts that currently run on the file cache are now stricter about using one runtime-safe file-cache directory instead of reusing stale storage/framework/cache/data trees with bad ownership, which avoids the repeated Unable to create lockable file failures that could break endpoints such as public RSS JSON reads./feed, /feed/c/*, and /feed/cards/* pages stalling when one category/feed has grown a very large article history.bin/install-browserbot-stack.sh. It now focuses on the runtime that the current Socdemo flow actually uses today — Node from NodeSource, filtered apt-based browser dependencies, Xvfb, Playwright, and one project-local Playwright Chromium cache under storage/app/browser-automation/playwright-browsers — instead of relying on a split chain of older helper installers or the current Unix user's ~/.cache/ms-playwright folder.play/socdemo-play.sh no longer uses browserbot.sh for the actual playback step. It now starts the recorded Playwright script directly, blocks pure headless mode for this extension-backed flow, refuses to create a fresh empty login profile silently, can restore the runtime profile from a matching profile seed when available, and can self-heal the local Playwright Chromium cache when that browser download is missing./usr/bin/chromium-browser launcher is now treated as broken for this flow instead of as a valid browser. The current Socdemo runner explicitly rejects Snap stubs and prefers a verified real Chromium binary, including the project-local Playwright Chromium cache, while the prepare/doctor helpers can still fall back to a verified Google Chrome binary for manual profile setup when Chromium is unavailable on that host.apt-get update && apt-get install -y xvfb when xvfb-run is missing. They now point to a dedicated bin/install-xvfb-safe.sh helper that uses only the normal distro package sources, so one broken unrelated PPA does not block Xvfb installation on remote hosts.--with-deps apt flow on Linux hosts. It installs the common browser dependency packages through Tools' filtered apt wrapper first, then downloads the Playwright browser normally, which prevents one broken unrelated PPA from aborting the full browser setup. That dependency pass now also resolves newer Ubuntu t64/virtual package names automatically (for example libasound2 → libasound2t64 when needed) instead of stopping on no installation candidate, and browser downloads now default to one project-local cache under storage/app/browser-automation/playwright-browsers instead of the current user's ~/.cache/ms-playwright. The browserbot/install repair messages now also use explicit project-root script paths instead of assuming the current shell is already in the Tools root.Executable doesn't exist or spawn ... EACCES error..net aliases as the same environment family as tools.tornevall.net, not just for cache-driver selection but also for public URL fallback logic and the DISABLE_SSL_VERIFY_TEST host detection used by the shared HTTP client diagnostics.*.ns.cloudflare.com can switch straight to the Cloudflare-backed read/write path instead of first failing with a raw AXFR-style transfer error./feed overview can now show significantly more feeds per page before pagination kicks in, which makes the new lazy-loaded category-card layout more practical when many feeds would otherwise spill onto page 2 immediately./feed now include their cached category AI summaries again, and those summaries are opened directly in the loaded category content instead of disappearing behind the feed list./feed now makes follow-up questions clearer: choosing one recent answer as follow-up context now opens the question panel, focuses the input field, and shows an explicit notice explaining that the next question should be written in the field below. The recent-questions block also now has a refresh button./feed category cards now expose their lazy-load helper safely to the global page script again, so opening one collapsed category no longer fails with a browser-side ensureCategoryContentLoaded is not defined error.memcached extension is actually loaded. Plain PHP aliases/stubs no longer count as a valid Memcached client, which prevents false-positive fallback attempts that could otherwise crash during cache bootstrap.storage/framework/*, so file-cache lock errors are easier to catch immediately after maintenance instead of later during page requests.tools.tornevall.com keeps the ordinary default cache driver (currently file-based in local/dev setups), while tools.tornevall.net can switch to a separate CACHE_DRIVER_NET value such as Redis when that host/runtime supports it./docs, /docs/en, /docs/sv, and individual docs pages) plus RSS outbound redirects (/out/{contentId}) now skip the nonessential database-backed web extras such as footer online-session counters, per-request job triggering, and visit tracking. That means those lightweight public pages can still open even when the ordinary site database is under temporary pressure.SESSION_DRIVER and SESSION_CONNECTION environment settings again instead of forcing database sessions everywhere, so local/dev environments can switch to file sessions without code edits./docs/sv and /docs/en) now open their real language indexes again instead of sometimes falling into a 404 by being mistaken for an ordinary documentation page slug./feed and /out/{contentId} no longer fall into a 500-style bootstrap crash on Windows/WAMP hosts just because Laravel's cache-manifest path was interpreted incorrectly. Tools now keeps the normal local bootstrap/cache path untouched when it is already writable, and the public error page can also fall back to a minimal built-in HTML response if the view layer never finished bootstrapping.phpredis client but do not actually have the Redis PHP extension loaded now fall back away from Redis automatically, so pages like /feed can still use the normal file/array cache path instead of crashing with Class "Redis" not found./feed overview now keeps category cards lightweight on first load. Feed rows, category AI summaries, and per-site AI summaries are fetched only when one category is actually opened, which reduces the initial page weight and makes the collapsed overview noticeably faster./services now opens the live-control page directly, so the gateway runtime routing overview and generated run-after config are easier to find./admin/virtualbox overview now consolidates each host into one inventory inspection instead of opening several noisy follow-up sessions per page load, which makes SOAP failures easier to read and reduces duplicate error spam.bin/fixnode.sh, bin/install-playwright-laravel.sh, and bin/install-chrome-headless.sh) are now more resilient on Ubuntu/Debian hosts with broken third-party APT repositories. They now isolate their package-manager work to the needed distro/vendor sources instead of failing just because one unrelated PPA is unavailable, and the Chrome installer now auto-detects the current Laravel app root instead of assuming one older default path./api/firewall/runtime/{host} plus /api/firewall/runtime/{host}/run-after.conf). The /firewall/live admin page can now show the grouped generated run-after route config together with synced netplan / legacy network-interface snapshots when the shared serverinformation path is mounted on the Tools host./feed overview now does less repeated counting work per page load. Feed entry totals, first/last discovery timestamps, and the edited-post counters are now aggregated more efficiently and cached for short periods, which reduces the slowdown that could happen when the overview page had to recalculate those counts for every visible feed row.Chromium cannot read and write to its data directory failure.Class "Log" not found cascade.bootstrap/cache or storage/logs path./out/{contentId}) no longer risk turning one optional RSS entry clicked analytics log write into an internal error when the ordinary Laravel log channel is unavailable on a read-only host./me.php callback no longer tries to write its temporary IP marker directly into the public web root, so that endpoint now keeps working even when the deployed public/ mount itself is read-only.__getFunctions() plus __getTypes(), try wrapped login/logout request objects before raw positional arguments, and expose an admin-only Debug SOAP action with sanitized WSDL/function/type/request diagnostics when one host still refuses login.storage/framework/views is read-only on the host, which prevents those requests from crashing just because the deploy mount itself is immutable.user-mailout queue, and the browser-side send/test actions can now return immediately instead of waiting for the whole mail run inline.SUPPORT_EMAIL, support@tornevall.net by default) instead of relying only on the generic framework sender./openai/access/request directly now sends signed-in users back to the built-in request card instead of falling through to a generic error-looking page, and failed request submissions now return a user-facing error message instead of an unhelpful 500-style dead end./admin/openai.provider_tools_openai) for different internal AI jobs. Each token can be named per assignment and rotated or killed individually, while the real upstream OpenAI key remains server-side inside Tools.provider_openai), so approved users can split internal Tools AI usage across multiple task-specific bearer tokens without reusing one shared token everywhere.POST /api/vbulletin/onboarding/invite/complete) is now explicitly documented as a token-free frontend hook endpoint, and it now accepts either a raw invite code or a full onboarding URL in invite_code by stripping the URL down to the real token before lookup.header_head hook arguments, adapted field66 bootstrap code, a direct fetch() example, and a styled success-popup example.invite code not found stop behavior, FormData POST transport to Tools, and popup feedback for both successful and failed verification./snowball -> /snowball/forum redirect, the header_head-driven logo swap, early logo-hide CSS, Tools bundle preconnect/preload hints, and the current member-widget setup.POST /api/vbulletin/onboarding/invite/complete for a future vBulletin frontend completion script. The endpoint can reuse or create the matching onboarding request from an invite code plus forum identity hints, validates any configured invite-code profile-field match before it trusts the resolved forum account, and only auto-activates forum access when the existing onboarding config already allows safe automatic approval.{{tools_support_email}}, so invite emails can point to the shared support address (support@tornevall.net by default) without replacing the existing {{tools_user_email}} placeholder for the current Tools user./admin/mail-support-assistant now disappear immediately after delete instead of waiting for a full page reload, and the threaded Tools case page is more defensive about older over-trimmed reply-aware bodies by falling back to the fuller stored plain-text body when needed./admin/mail-support-assistant. Operators can also choose to delete already stored synced threads for that sender at the same time, and standalone clients now receive the same mailbox-level ignore list through GET /api/mail-support-assistant/config so future mail from those sender emails is skipped immediately.facebook.com/messages) instead of the separate messenger.com site when operators use the Messenger shortcut button./vbulletin/onboarding/keys for that same recipient email and send one follow-up mail with the delegated key page plus the currently visible invite list for that slug. The invite-outreach composer also waits until you leave the current template/sender field before it saves, so typing in longer message templates no longer jumps the cursor out of the textarea mid-edit.Kod, Länk, and Person?, either as rich HTML for Google Docs paste or as plain-text fallback.media_file validation error instead of leaving users with the generic “failed to upload” wording. Whisper status payloads also expose the effective upload limit after PHP/body limits have been taken into account.body in separate mount_* roots. That means later preview/original-post text is much less likely to stop updating until the operator presses Find preview element manually./donate landing page instead of sending the navbar straight to PayPal only.chromium-browser only exists as a Snap wrapper and the service account's configured home directory does not actually exist, browserbot now skips that broken Chromium path when possible, falls back to real Chrome if it is installed, and otherwise explains that the real fix is to create the account's home directory or run under a user with a valid home./groups/*/participant_requests, and stale participant-analysis follow-up state is cleared so an older analysis does not linger above unrelated Facebook pages.CometPhotoRootContentQuery), and the floating detected-context/debug views now show explicit comment-thread counts plus captured comment excerpts so it is clearer that deeper thread context really made it into Analyze user.getParticipantHistoryForSummary / participant-history helper chain when it tries to render earlier moderation-history badges or inject that same history into Analyze user context./groups/<id>/user/...) as a fallback card-identity signal and climbs farther up through Facebook's deeper moderation markup, so visible request cards are less likely to disappear when Meta changes the surrounding wrappers again.gpt-4o as the primary model.--open mode now forces a sane visible browser window by default (1600x1000 at 40,40) so older persistent profiles that remembered a tiny or off-screen Chrome window no longer reopen as a nearly invisible blank box. The geometry can still be tuned or disabled through BROWSERBOT_WINDOW_SIZE, BROWSERBOT_WINDOW_POSITION, and BROWSERBOT_DISABLE_DEFAULT_WINDOW_BOUNDS.collectParticipantContextNeighborhoodLines function while preview/comment follow-up context is being assembled.Cards enhanced: 0.--no-first-run plus --no-default-browser-check always, and --no-sandbox plus --disable-setuid-sandbox automatically when the launch runs as root. The same shared launch builder now also keeps explicit unpacked SocialGPT extension loading (--disable-extensions-except=... / --load-extension=...) aligned across browserbot open mode, SocialGPT playback, and the persistent Playwright record/run helpers.?wsdl is reachable but PHP SOAP fails with a Parsing WSDL / Couldn't bind to service style bind error.logon, which fixes connection attempts against hosts that answered with Method 'ns1:logon' not implemented even though the WebService itself was reachable.xvfb-run when no DISPLAY/WAYLAND_DISPLAY is available on the host, so server-side socdemo-play and prepare-socdemo-profile flows no longer fall straight into Chrome/Playwright's raw “Missing X server or $DISPLAY” crash when xvfb is installed.Cannot set properties of undefined (setting 'position') while the helper starts./groups/<id> instead of reusing one shared rule text for every group. The per-group rules map is now also saved through Tools extension settings so it can sync between installs for the same user/token, and on the actual participant-request page only the exact current group's saved rules are shown/used there.fb-socdemo2-stats now explains much more clearly what a fresh host actually needs: the profile seed under storage/app/browser-automation/profile-seeds/... only carries cookies/session/local state, while the SocialGPT extension itself is still loaded as unpacked code from projects/socialgpt-chrome. The helper scripts now point more directly at submodule bootstrap, and play/socdemo-doctor.sh shows whether the missing piece is the submodule, the profile seed, or the runtime profile.browserbot.sh --open flow is now more forgiving on Linux hosts where chromium is missing but Chrome is installed: a request for chromium can now fall back to the installed Chrome binary instead of stopping immediately, and the startup output now shows the requested browser plus the effective browser/executable. The SocialGPT socdemo helper text was also corrected so it no longer assumes every checkout git-ignores full runtime profiles; profile seeds remain the recommended cache-stripped format for moving sessions between hosts.play/prepare-socdemo-profile.sh and play/socdemo-play.sh no longer die immediately just because chromium is unavailable on a host that already has Chrome installed.play/socdemo-play.sh is now self-contained for the operational path instead of depending on the extra shared socdemo helper file just to restore the seed, find the SocialGPT extension, and resolve the browser fallback before browserbot starts.browserbot.sh --open automatically adds the Chromium-family no-sandbox flags when it is launched as root, and play/prepare-socdemo-profile.sh no longer aborts the whole clone/export workflow just because one browser-open step failed.INSTALL_CHROMIUM=true bash bin/install-playwright-laravel.sh (or the same through bin/install-browserbot-stack.sh) now tries the host's chromium-browser / chromium apt package names instead of leaving every host to install Chromium manually.--load-extension sideload is the same thing as a real profile installation. play/prepare-socdemo-profile.sh now opens the source profile on chrome://extensions/ for a real Load unpacked install, and the later target-profile verification opens without --with-socialgpt so the copied profile is tested in the same persistent-extension state that playback will actually use.POST /api/ai/socialgpt/respond can now return a friendlier user_message plus additive retryable, error_code, and upstream metadata when Tools hits a temporary OpenAI timeout/connection problem, instead of only exposing a raw transport error. Verify/source-check flows can also keep a preliminary answer visible and attach a notice when only the last OpenAI refinement step times out.browserbot.sh --export-profile-seed writes the reusable part of one prepared browser profile under storage/app/browser-automation/profile-seeds/<name> while skipping browser caches and runtime lock files, and browserbot.sh --copy-profile-from-seed can restore a live runtime profile from that exported seed on a fresh host. The SocialGPT helper scripts now use the same flow automatically.bin/fixnode.sh reinstalls the NodeSource apt repository with an explicit keyring and signed-by= source entry instead of relying on the older setup-script pipe, which fixes the common NO_PUBKEY 2F59B5F99B1BE0B4 failure, and the Chrome helper now ships with normalized shell line endings so bin/install-chrome-headless.sh no longer trips over a parser error before the browser install even starts.vboxwebsrv SOAP endpoint instead of SSH/VBoxManage. Server cards now default to port 8443, the login fields are labeled as API credentials, and the admin help text/docs explain that the configured host/port should point to the WebService proxy endpoint./feed and on site-specific feed pages now also show when web search was actually used and list any returned citation links.content:encoded, dc:creator, guid, pubDate, and category data are handled more carefully, entity-escaped HTML bodies are normalized without aggressive stripping, and duplicate checks can now prefer GUID identity before falling back to legacy link-based matching./status requests, stuck polls are aborted and retried with backoff, background-tab throttling is handled more cleanly, and the browser now accepts a completed background process response immediately when it already contains the final answer.mount_* roots, and the floating helper exposes a new Find preview element action that scrolls to and highlights the currently matched preview dialog.Checking now… spinner.preferred_body.text, body_renderer.text, original-post message text, author names, timestamps, comment/post ids, and Facebook URLs — and merges that normalized preview context into the user-analysis request before DOM-only fallback is used.browserbot.sh now prefers an installed Chrome binary for SocialGPT/extension-backed Playwright runs when a script still says chromium but Playwright's bundled Chromium is missing, and the startup log now shows both the requested browser and the effective browser/path used for that run.appendChild-style extension crashes on pages such as forum threads.admin/social-media-tools now send signed-out visitors to the normal login flow again instead of showing the generic branded 500-style public error page.provider_socialgpt) that can be generated for the Chrome extension without granting the account direct OpenAI API access. Direct OpenAI-backed endpoints still require the separate OpenAI access request and approval flow.docs/pre-email.md, and includes a Send test to me only action so admins can dry-run a real message to themselves before broadcasting to users.dnsbl-engine operational reports can now be delivered immediately, hourly, or daily through SPAM_REPORTS_FREQUENCY, allowing noisy per-run reports to be grouped into digest mails.SEND TO USERS confirmation phrase before any real broadcast is sent.Cannot find module 'playwright' stack trace.tools.tornevall.com are no longer forwarded into the Laravel error notification stream when they are the noisy Maximum execution time ... exceeded class of warning; other hosts and other error types remain reportable.ReferenceError messages.independent_verification_missing outcomes caused by adapter-side payload/detection gaps./groups/*/participant_requests moderation pages: visible-card scans are more conservative, card scoring is cached more aggressively, the floating scanner now shows scan timing, and the inline actions now prefer a smaller placement near the card's existing action / ... area when Facebook exposes a stable anchor there./oauth/microsoft/*, there is a new generic status endpoint at GET /api/microsoft/auth/status, and the shared Microsoft / Graph app now reports both personal-account and Microsoft Entra work/school account support. Older /oauth/microsoft-todo/* and /api/microsoft-todo/oauth/start paths still work as compatibility aliases.@author tagging is still counted inside the final posted length.search RSS. When that kind of generic feed-summary prompt has no real topic keywords, Tools now falls back to recent feed items instead of missing the RSS lookup entirely.COUNT(DISTINCT content_hash) aggregates on every nearby page load. Manual RSS purge actions now also clear those cached counters immediately after cleanup.audit_x_bot no longer forwards routine poll noise by itself, and Slack mirroring is now reserved for actual posted-reply outcomes rather than every recorded X-bot event.audit_x_bot, even when the ordinary manual-review Slack toggle is off. This makes the recorded moderation/runtime event stream available as an audit feed instead of only as direct review notifications.{ "message": ... } payload when one recovery/rewrite step answered in machine-readable form instead of plain text./out/{contentId} feed redirects now cache resolved outbound article links and skip unnecessary feed-source lookups when the stored link is already absolute. This makes repeated article clicks much less dependent on the RSS database and reduces timeout risk during heavier crawler traffic.participant_requests helper now prefers stable left-column moderation rows such as comment/preview and unanswered-question lines before it falls back to the top-right ... area, and its DOM observer now stays scoped to the relevant request content root so heavier moderation pages trigger fewer unnecessary rescans.SG participant-request wording is gone: the main card action now explicitly says Analyze user, and the Toolbox import now structures visible group/friend clues, visible membership questions, unanswered-question state, and profile/background hints before the raw card text./settings/integrations/microsoft-todo, so a successful callback no longer drops the user onto a fresh login wall just because the earlier browser session was gone.@Tornevall is now always treated as protected and can no longer be effectively blocked by X-bot opt-out handling.gpt-4o fallback.stop or Swedish sluta are no longer treated as opt-out commands unless the wording clearly asks the bot to stop replying/engaging. Bare stop / sluta wording on its own no longer counts as opt-out either. Older automatically stored false-positive opt-outs can now also auto-release when the same author later directly engages the bot again with a normal mention. The admin interaction cards still include an Opt in again action for manual recovery when needed.GET /api/microsoft/oauth/start. The older GET /api/microsoft-todo/oauth/start path still works for backwards compatibility, but the generic path is now the recommended one when the same Microsoft app registration is meant to serve the wider Tools platform.MAX_WRITE_OPERATIONS_PER_MINUTE quota failures in the extension background worker.max_reply_length=0 no longer means “send overlong replies unchanged”: Tools now still enforces the normal X hard reply limit before posting, even when the extra local Tools cap is disabled./docs/en/toolsgpt-changelog, intended as the public reference when people ask how the X bot has evolved over time without exposing internal runtime details.participant_requests layout: the per-card Analyze in Toolbox / Verify facts helper now climbs a wider DOM range, skips the bulk-action toolbar more reliably, and restores the saved Tools-side checkbox state on the Facebook Admin Tools page more consistently. That same admin page no longer renders the same green success confirmation twice.GET /api/microsoft-todo/oauth/start, and the callback URL can now finish the connection even when the browser has lost its active Tools web session, as long as the signed OAuth state still matches the original user/transaction. The integration page now also explains the common unauthorized_client mismatch between tenant choice and Azure app account-type support more clearly./groups/*/participant_requests now wakes up from the Tools-side participant scanner switch more reliably.@ToolGPT just to be ingested for review/reply handling, and explicit mentions are no longer misclassified as keyword-only just because a watch keyword matched too.participant_requests pages when the feature is enabled from Tools: visible participant-request cards get compact Analyze in Toolbox and Verify facts actions, and the participant-scanner switch is managed from the Tools-side Facebook settings instead of a separate local popup toggle./admin/security/vbulletin is the clear preconfiguration page for groups, templates, destination, and other defaults, while the admin/delegated key pages now stay focused on choosing one onboarding flow, one recipient, and only optional advanced overrides. The older bulk copy-list blocks for active keys/direct URLs have been removed from those key pages.dnsbl-engine-tools-client) so internal tools or automation clients are counted as bots/crawlers everywhere the live online counters are shown, and bot rows now surface clearer bot names plus categories in /online and the admin session view./docs/en/vbulletin-registration-changelog, intended as a compact operator summary that can be copied into external notes or a separate Google Drive document.support@tornevall.net plus the inviter if the code or link does not work./docs/en/playwright, focused specifically on when to use stored browser-automation scripts, browserbot.sh, record/open/profile-clone flows, and the Playwright E2E runner.[Ärende MSA-…] style subject tag instead of sending a plain Re: subject.selected_rule* fields as before, and older payloads that still send selected_rule_id=0 are now normalized instead of being rejected with a generic 422, so unmatched mailbox messages can still appear in the centralized Tools case view.browserbot.sh now supports explicit profile seeding/cloning for multi-account Playwright setups: --profile is available as a shorter alias for --profile-directory, --copy-profile-from can seed one persistent profile from another before open/record/run, and --clone-profile can copy a prepared base profile into a new target profile without starting the browser yet.422 when unread mail is reported back into the centralized Tools case view.rss.posting.handle permission. The new /feed-admin/posting-queue and /rss/posting-queue UI lists the latest entries per feed/category with AJAX checkboxes for Handle group, Handle page, Handled group, and Handled page, and matching web-session API endpoints now live under /api/rss/posting-queue/items* so browser automation or another internal UI can mark entries handled as soon as they were posted.browserbot.sh --open-socialgpt shortcut that opens real Chrome with the local projects/socialgpt-chrome extension and the shared persistent default profile, making it easier to keep SocialGPT available from the default reusable browser-automation profile.browserbot.sh --open --browser chrome with the same persistent profile, while unpacked local extensions can be loaded directly from a local folder instead of going through the Chrome Web Store.profile_directory, Tools automatically falls back to that script's key as the saved profile name, and a clean one-off run now requires an explicit --fresh-profile request.bin/fixnode.sh waits for active apt/dpkg locks before repairing Node.js, verifies that node, npm, and npx really exist after installation, and the Playwright installer now points more clearly at the Node repair helper when npm tooling is still missing.browserbot.sh wrapper so admins can launch one stored browser script from shell by key or exact name instead of rebuilding the Playwright command by hand. The browser automation admin pages now show copyable shell examples, and persistent login/cookie reuse is now documented more explicitly around the stored profile_directory field.Approvals · join requests, Approvals · pending posts, Rejects · join requests, Rejects · pending posts) for a faster overview, while the older fully separated automatic/manual outcome view still remains available./online views./admin/site-statistics links now redirect to the current /admin/security/site-statistics route, the admin/welcome shortcuts now point at the correct route, and the page itself has a stronger control-room style with quick links for adjacent admin tools./admin/browser-automation/playwright for the project-level tests/e2e suite. Admins can inspect test files, run the whole suite or one allowed spec, choose base URL / skip-webserver / persistent profile settings, and review stored run history with stdout, stderr, exit code, HTML report links, screenshots, videos, and traces when those artifacts exist.storage/playwright/*, a reusable persistent-session test helper for specs that should keep login state between runs, a dedicated bin/playwright-record-persistent.sh codegen helper, and a matching test:e2e:codegen npm alias without replacing the existing Mix/Vite project scripts./docs/en/x-bot, the public services page links to that guide, and the guide now explicitly explains that ToolGPT on X is the public-facing account identity for the Tools-backed X Mention Bot.message, can_reply, status) instead of only raw prose, so recent interactions and API consumers can see when a candidate is actually replyable, why a refusal happened, which GPT-5/GPT-4 model path was chosen, and which alternative candidates were tried. GPT-5 reply generation now escalates reasoning effort step-by-step before falling back to gpt-4o, the recent-interaction history now uses badge-heavy expandable cards for that decision/rerun data instead of one cramped table row, and Facebook admin activity queue sends in the SocialGPT browser extension now retry directly to Tools when the normal extension-runtime handoff stalls./feed and /feed/cards/{categorySlug} now merge category analytics by the category slug before picking the visible edition, so the newest current-bucket analysis still wins even if older cached rows for the same category were saved under slightly different name/case variants such as Right Wing vs Right wing./feed and feed-card analysis selection now prefers the newest generated current-bucket edition before older default-marked variants, so fresh daily analyses no longer get hidden behind stale cached defaults./api/managed-scripts/feed* now accept additive feed_ids / feeds, categories / groups / category_slugs, and limit query parameters, and expose the normalized values back to the bundle/API response as query_context.filters.nodeid / forumids) when trying sibling rss2/rss/atom candidates, and feed probing now follows redirects more consistently for those external feed URLs.needs_attention threads more clearly in admin, and lets admins delete one synced thread directly or bulk-delete older synced threads by age/status from /admin/mail-support-assistant.external?type=rss2... and external.php?type=RSS2..., including relative feed links discovered from forum pages, so those sources are now stored as ordinary rss feeds instead of falling back unnecessarily./online page now separates likely bots and crawlers into their own section, shows a dedicated bot counter, and keeps automated traffic out of the ordinary guest list so the human online overview is easier to scan.https://forum.tornevall.net as the fallback when nothing more specific is configured.403). Tools now keeps the onboarding request, links the matched forum account when possible, and moves the request to Pending review so an admin can complete the approval later instead of the applicant seeing a raw exception page.core/api.php save path after the scoped secondary-group change, so the affected forum account is rebuilt immediately and the old manual “open user in vBulletin and click Save” workaround should no longer be needed.profilefield records again instead of being limited by raw userfield column discovery, so configured custom fields such as field66 can reappear in the onboarding admin/key-generator selectors even when older leftover userfield columns would otherwise make the list misleading.155 when an onboarding config has no explicit allowed-group list, and both the admin key generator plus the delegated key-creator page can now create invite links in bulk with either no expiry, one exact expiry timestamp, or an extend-by-days lifetime. Batch-created labels are auto-numbered, while account-bound profile-field invite writes are still intentionally limited to one key at a time so one forum profile is not overwritten repeatedly.usergroup table when configuring bounced-email handling, so admins can choose the target/exempt groups by real name instead of guessing numeric ids. The same page now also documents the exact membergroupids write format: one plain id when the field was empty, otherwise a comma-separated list without spaces.2026_05_02_150000_create_vbulletin_onboarding_key_profile_assignments_table is now extra defensive when the table already exists, so repeated or partially completed deploys do not stop on a duplicate-table error just because one environment created the table before the migration record was stored. The duplicate-table guard now also survives wrapped exception chains instead of relying only on one direct exception type.field66), and newly created invite keys can target one known forum user so the code is written into that user's profile field and the onboarding request can link to that forum account immediately before applying the configured secondary group.English or Svenska), and the public slug page, onboarding form, and bookmarkable status page now open in Swedish when a Swedish onboarding key/link is shared./admin/security/vbulletin/onboarding/keys that focuses on referral/one-time keys plus welcome-message presentation./vbulletin/onboarding/{slug} (enter key manually) and /vbulletin/onboarding/{slug}/{inviteKey} (key prefilled), old token-only links can redirect to the canonical slug URL, and expired/wrong keys now fall back to a friendlier onboarding-key prompt instead of throwing a raw exception page./vbulletin/onboarding/keys./api/whisper/transcribe/*, including required callback_url, terminal callbacks, and queue-origin metadata (queue_channel).whisper.api permission and Whisper API token flow, and tightened the public Whisper documentation so it focuses on the external contract instead of internal runtime details.analysis_language, supports multi-select translation_target_languages[] for automatic post-transcript OpenAI translations, treats diarization as enabled by default per job unless the operator explicitly disables it for that job, and shows more visible heartbeat-driven progress updates while Whisper is still transcribing between raw output lines.WHISPER_YTDLP_JS_RUNTIMES=node / deno specs in addition to runtime:/full/path, php artisan whisper:doctor now reports the exact missing/non-executable runtime path instead of only a generic JS-runtime failure, and bin/install-whisper-venv.sh now prints browser/proxy env keys plus validates the current .env Node/Deno/browser runtime paths against the binaries it installed.php artisan whisper:doctor now reports Laravel-config, process-env, and effective WHISPER_YTDLP_PROXY values separately, while Whisper runtime attempt lines show proxy=(not configured) when the artisan worker did not actually receive a proxy; this makes stale config-cache / wrong-runtime-env cases obvious when manual yt-dlp --proxy ... works but queued Whisper jobs still hit YouTube 429.WHISPER_YTDLP_BROWSER_PATH through to yt-dlp as --browser-location when browser-cookie mode is enabled, keeps ordinary temporary/file-based cookie fallbacks behind that browser phase instead of making browser mode all-or-nothing, exports WHISPER_YTDLP_PROXY into the child-process proxy env as well as the CLI flag, and summarizes repeated YouTube 429 / bot-check / missing-runtime failures much more cleanly in stored operator-facing error text.WHISPER_YTDLP_PROXY, can prefer --cookies-from-browser through env (WHISPER_YTDLP_COOKIES_FROM_BROWSER plus optional WHISPER_YTDLP_BROWSER_PATH) instead of relying on hardcoded browser-path checks, falls back cleanly to Netscape cookie files when that browser probe is unavailable, and the URL-title prefetch path now uses Laravel-8-compatible HTTP client options instead of the unsupported PendingRequest::connectTimeout() call.source_label for pasted media URLs, can ask OpenAI to shorten noisy/overlong page titles from fetched head/body content before falling back to a generic URL label, lets transcript owners queue diarization-only reruns from the detail page over AJAX, and now tries transcript analysis through gpt-5.4 with reasoning before falling back to the normal 4o-style model path while keeping Swedish transcripts much more aggressively in Swedish.source_label / source_note) fully over AJAX, and when a completed transcript exists the same owner can also ask OpenAI to suggest better text directly into those fields before saving.source_label from pasted remote URLs (YouTube/oEmbed first, otherwise page metadata such as og:title / twitter:title / <title>), falls back to the existing URL-derived label style when no clean title is available, respects manually edited labels instead of overwriting them, and now uses a clearer responsive form layout for URL/upload/model/language input.WHISPER_DIARIZATION_ENABLED=true, stores additive diarization metadata plus speaker_aware_transcript / transcript_segments[].speaker_label, keeps transcripts completed even when diarization fails, and extends php artisan whisper:doctor with masked Hugging Face token presence plus pyannote.audio / torch import checks./shared/whisper/transcript/{token}, so operators can hand out a clean transcript view showing only title/source info, transcript analysis, and full transcript text without exposing the normal job-progress/runtime UI.cpu_threads_configured, processing_elapsed_seconds / processing_elapsed_human, and observed_throughput_multiplier / observed_throughput_summary, while the live runner detail now logs the active model/language/thread count/clip duration before transcription and reports elapsed time plus realtime throughput when Whisper finishes.source_duration_seconds plus a human-friendly source_duration_human label so operators can see clip length before opening the full transcript.WHISPER_CPU_THREADS, which appends --threads <n> to the current Python openai-whisper CLI and mirrors the same cap into common CPU math-library env vars so a 4-vCPU host can be pinned to 4 transcription threads more predictably.source_label and source_note metadata, so URL-based jobs can be given a human-readable title plus free-text context when the fetch itself is vague or unreliable.bin/repair-laravel-runtime-permissions.sh can repair storage/* plus bootstrap/cache ownership/permissions automatically from cron.analysis before any manual analyze action is triggered.php artisan whisper:process now takes a host-local non-blocking flock in addition to the existing Laravel cache lock, so cron/terminal runs on the same host cannot overlap even before the queue lock is reached.restart now only re-queues the job for the next worker pass, and the admin/API run-now helpers are now queue/reset-only so terminal/cron remains the only execution environment.php artisan whisper:process --unlock-only, plus one-shot unlock-and-run via php artisan whisper:process --unlock --limit=1 when a stale whisper:queue-runner lock blocks processing.completed or reaches final failed, including job id/status/source and the latest failure text when applicable.WHISPER_PROCESS_PATH, WHISPER_PROCESS_HOME) for yt-dlp and Whisper subprocesses so web/queue execution can match terminal behavior more reliably.WHISPER_YTDLP_DENO_BIN), with clearer diagnostics when configured JS runtimes are invalid or missing.php artisan whisper:doctor for runtime diagnostics (binary paths, version checks, effective PATH/HOME, JS runtime availability, cookie file readiness, and whisper storage write access).anonymous_temp run before the newer JS-runtime, extractor, and format fallbacks kick in, matching the safer operator expectation that a simple public-video attempt should happen first.WHISPER_YTDLP_JS_RUNTIMES and WHISPER_YTDLP_NODE_BIN, and will try to auto-detect node / nodejs from PATH for YouTube-style downloads when no explicit runtime override is configured.anonymous_temp (an empty temporary cookie jar), only falls back to db_temp and then raw_file_temp on explicit access-required failures, and always hands yt-dlp a fresh temporary cookie file rather than the original stored DB cookies or raw storage cookie file.-f arguments, now keeps the normal first attempt free from the accidental forced bestvideo+bestaudio selector, and can explicitly try database-managed cookies first and WHISPER_YTDLP_COOKIES_FILE as a later fallback phase on the same job before finally failing.n challenge hints now explain more clearly that this is YouTube's obfuscated player check and that yt-dlp may need a working JavaScript runtime such as Node.js to evaluate it, depending on the current yt-dlp extractor path.reasoning_effort when the active OpenAI chat model rejects that argument, so /api/whisper/jobs/{jobId}/analyze and the web detail view no longer fail immediately on models/providers that do not expose reasoning controls.n challenge solving failed, Only images are available for download, and Requested format is not available, using a safer player-client combination plus an audio-first format selector before the job is finally marked failed.liveness heartbeat metadata (state, summary, heartbeat_at, last_output_at, runner_id, current_step, stale_after_seconds, is_stale) in the web/API queue payloads, so operators can tell whether a downloading / transcribing / finalizing row is still alive, only quiet, or likely crashed.downloading / transcribing / finalizing can now be cancelled safely from the web/API surfaces before deletion. The worker observes a stored cancel request, stops the active yt-dlp/Whisper process cooperatively, marks the row as stopped instead of retrying it, and only then allows ordinary delete/restart handling.Array to string conversion when OpenAI returns structured content blocks instead of one plain string. The admin cookie box can now also append/merge newly pasted Netscape cookie rows into the stored yt-dlp jar instead of only replacing it.n challenge solving failed / missing-JavaScript-runtime warnings more explicitly, stores a clearer operator hint in the live queue progress, and supports an optional WHISPER_YTDLP_EXTRA_ARGS host override for stubborn YouTube player-client extractor issues after yt-dlp itself has been updated./admin/whisper, the queue stores it securely, yt-dlp uses a temporary cookie file during supported YouTube/login-bound downloads, and GET /api/whisper/status now exposes additive config.ytdlp_cookies metadata.php artisan whisper:process is now self-locking even outside Laravel scheduler, so a minute-wise cron run no longer steals work while another Whisper run is still active. The command now logs to storage/logs/whisper-queue.log by default instead of depending on terminal output, and busy lock collisions are recorded as warnings instead of starting a second runner.WHISPER_DEFAULT_MODEL falls back to large, the web UI defaults to that best-quality choice, and the API now also exposes POST /api/whisper/jobs/{jobId}/analyze plus additive analysis metadata on job payloads for on-demand transcript summaries./api/password-manager/*, covering entry list/read/create/update/delete for the logged-in owner's own encrypted vault rows./api/account/login, returns masked list rows by default, only exposes decrypted secret_payload on explicit detail/create/update responses, and marks responses Cache-Control: no-store, private.The message expresses... or The post suggests..., so ordinary ask/explain replies are pushed back toward a concrete answer instead of narrating what the post said.@username mention.401 (authentication rejected), and reports authorization_header_present / bearer_token_present explicitly instead of the earlier ambiguous blank Has bearer token field.reply_prefix_with_author correctly, so visible @username tagging stays off unless an operator explicitly enables it.stage_label, stage_detail, and a short runtime_log tail, so the user/admin queue views and CLI runner output can finally show what yt-dlp or Whisper was doing instead of only ending on a generic failure sentence.PHP_DEPRECATED_NOTICES=0 as a direct way to mute PHP deprecation noise on newer runtimes, while still honoring the older PHP_SUPPRESS_DEPRECATIONS compatibility flag./password-manager, with encrypted per-user vault entries for logins, secure notes, and a cautious first payment-card mode. No extra permission is required, sharing is not implemented yet, and CVV/CVC storage is intentionally blocked.DELETE /api/whisper/jobs/{jobId}. This is especially meant for failed jobs that no longer matter, and the backend now removes the job row together with its stored Whisper project directory plus safe local upload/transcript files while refusing deletes for actively processing jobs.yt-dlp for YouTube and other explicitly supported page/video URLs instead of trying to transcribe downloaded HTML, direct-media HTTP downloads now reject obvious non-media Content-Type values before saving input.bin, the Python -m whisper path is tried before the plain whisper binary, successful-but-empty Whisper runs now report the expected TXT path plus stdout/stderr/files-found diagnostics, and GET /api/whisper/status can now expose additive config.ytdlp_configured metadata.php artisan whisper:process now supports --restart-failed and --job-id=<id>, the API now exposes POST /api/whisper/jobs/{jobId}/restart, admin/API run-now calls can now also take reset_failed=true, failed-job state is reset properly before retry, and stored execution errors now include a clearer installation hint when the host is missing a working Whisper binary/module. The built-in default-model fallback is now turbo instead of small for new environments, although an explicit WHISPER_DEFAULT_MODEL setting still wins..../photo/1 attachment URL. Internal X/Twitter attachment URLs like /status/.../photo/1 and /video/1 are also no longer treated as ordinary linked-page context when real tweet media is already attached.the commenter believes, the post says, or the tweet criticizes, and post-processing now strips those meta-summary lead-ins when they still slip through.Reply to this conversation is not allowed because you have not been mentioned or otherwise engaged... failure as a thread-engagement restriction: keyword-only public-post replies stay manual-only by default, reprocess can still run, and the stored error text now explains that the bot account was never explicitly pulled into that conversation.260 by default) when older rows are missing stored length metadata, instead of misleadingly showing 280 for those legacy rows.I cannot identify individuals refusals.@author prefixing as optional compatibility behavior instead of a reply-threading requirement, because live posting already threads through X's in_reply_to_tweet_id payload. New/default bot settings now leave that prefix off unless an operator explicitly enables it.….You are not permitted to perform this action., Tools now explains that this is normally an X-side write/reply permission problem for the app or bot token pair, not a character-length problem.max_context_posts up to 50, and max_reply_length=0 now means “no local Tools character cap” instead of failing validation or forcing the older trim range.Kortfattat!, briefly, short answer, and one sentence as a stronger concise-intent signal: the prompt tightens brevity rules, the AI token budget is lowered, and the generated reply is sentence-trimmed before final posting.webhook_crc_failed / webhook_receive_failed runtime events when CRC verification or signed delivery handling crashes, stores the latest webhook error in runtime settings, and can forward those webhook failures to Slack through audit_x_bot even outside the normal manual-review flow./admin/social-media-tools/x-bot* web routes instead of depending on the /api/social-media-tools/x-bot/* session path, which fixes the lingering 401 Unauthorized failures on blur/change saves for logged-in operators while still keeping the public API endpoints intact..com/.net style host drift that could trigger 419 CSRF token mismatch failures and surface a raw [object Object] error instead of useful feedback.max_reply_length hard not only when a fresh AI candidate reply is first generated, but again when a candidate reply is persisted/finalized and immediately before it is posted to X, so older stored candidates cannot slip past a newly lowered length limit.image_url inputs instead of relying only on media summaries / alt text, while still letting operators disable that forwarding explicitly from config.historical_context_max_posts separately from the much smaller prompt window, including 0 for effectively unlimited local archive depth while max_context_posts still controls how much is sent to OpenAI in one request./docs/terms-of-service page now exists for the full Tools platform, covering website use, APIs, AI-assisted workflows, automation features, credentials, third-party providers, and acceptable use./oauth/x/callback plus /oauth/x/callback-url, so there is a concrete callback URL available for X app registration even though the current X bot still uses static server-side X credentials instead of a browser-based OAuth completion flow.x_bot_rules table has not been migrated yet: the page no longer crashes, the rule panel explains that php artisan migrate is still needed, and fallback instruction/mood keep working until the table exists.php artisan x-bot:poll-mentions --now (and the existing run-x-bot* wrappers), while the admin Poll mentions now action now also ignores that interval and immediately runs the due auto-approve cycle./api/x-bot/webhook, with CRC verification on GET, signed delivery handling on POST, queue-based processing, and admin-visible webhook diagnostics such as last received/processed/error state.Tornevall when Only explicit @mentions is turned off, and those keyword-triggered posts go through the same helpfulness-/priority-based routing as direct mentions.ToolsGPT-vX.Y.Z style version label when users explicitly ask.yt-dlp into the job directory before Whisper starts, so media_path points at the real extracted audio file instead of a saved HTML page.yt-dlp completely; they continue to use the stored upload path directly.text/html, JSON, and other non-media content types) fail with a clear error before input.bin is written..txt transcript file, the stored error text now includes the expected transcript path, the output directory, the files found there, and the captured stdout/stderr from the successful run.ytdlp_configured=true|false, making it easier for clients and operators to tell whether the host is expected to handle YouTube-like URLs./vbulletin/onboarding, so people no longer need to discover it indirectly through the embedded admin vBulletin area./admin/security/vbulletin now includes a first real onboarding/admin layer in addition to the earlier failed-login cleaner and frontend script-box area.vbulletin.manage can now create referral or one-time onboarding keys, and each created key gets a public onboarding URL under /vbulletin/onboarding/{token}./vbulletin/onboarding/status/{statusToken}.forum_user_id, and limited approve/reject/revoke flows that add or remove only the configured secondary vBulletin groups.php artisan vbulletin:onboarding-sync every five minutes so exact email/username matches can link forum accounts automatically and optionally auto-approve when the configuration allows it./vbulletin/onboarding/messages.js, intended for direct vBulletin template/snippet use or for embedding through the existing vBulletin managed-script surface.POST /api/whisper/jobs now supports both the older source_url JSON/form flow and multipart/form-data uploads through media_file, while still rejecting requests that try to send both at the same time./whisper user page now includes an upload field next to the URL field, so local recordings and other non-public files can be queued directly from the browser.source_type, source_label, source_mime, and source_size_bytes, making it clear whether a job came from a URL or an uploaded file./admin/whisper now render uploaded filenames and MIME information instead of assuming the source column always contains a URL./api/dns/acme/* so console tooling can present, clean up, and explicitly stale-clean _acme-challenge TXT rows without pretending that a separate legacy ACME subsystem still exists.POST /api/dns/acme/present, POST /api/dns/acme/cleanup, and POST /api/dns/acme/cleanup-stale, and they reuse the same /api/dns/* auth and zone-permission model as the normal DNS record writes.cleanup-stale now supports a keep-list plus dry-run/cache-refresh flags, which gives operators a controlled way to remove older _acme-challenge TXT rows while preserving the currently active challenge values.php artisan dns:acme:cleanup-stale <domain> for the same one-owner stale-cleanup flow, so shell/cron-driven maintenance does not need to guess at raw nsupdate commands./api/acme/challenge/* routes./admin/security/vbulletin and /feed-admin, so operators can keep small frontend JavaScript customizations in one place instead of scattering them across ad-hoc snippets.script src, AI instructions, inline JavaScript body, enabled/disabled state, and sort order./feed-admin now also has a Feed script snippets / embeddable widgets section for /feed-related widgets and external site news boxes.GET /api/managed-scripts/{surface} and GET /api/managed-scripts/{surface}/bundle.js for vbulletin and feed.script src rows, executes inline snippets in configured order, and guards against duplicate inline/external initialization on the same page load.Tornevall outside direct mentions when Only explicit @mentions is set to No, and those posts are stored/reviewed through the normal interaction queue.gpt-5.4 with reasoning enabled by default and an automatic fallback to gpt-4o-mini when the primary model fails, rejects the reasoning flag, or returns an empty reply.ToolsGPT-v0.1.0, where the name comes from the synced X account identity and only the version suffix remains operator-configurable.php artisan x-bot:poll-mentions or the new repository-root helper scripts run-x-bot, run-x-bot.sh, and run-x-bot.bat, and the X bot docs now describe the manual --no-dispatch dry ingest mode plus the scheduler path./visit-stats page now presents ordinary web visits and tracked API requests as two clearly separated traffic groups instead of treating them like one mixed usage bucket./api/rss/* scraper traffic does not leak back into the visible API totals/trends./firewall/live host-list refreshes now start asynchronously and expose tracked progress/status polling, so the admin page can show visible per-list progress without blocking the whole browser request while DNS and RDAP/network lookups run.fwmaker and the Laravel-side live-rule renderer now both apply explicit strong-rule precedence per targeted child IP, so full_block wins over internet_block, which wins over allow_only_local, regardless of row creation order, while CIDR destinations remain intact in generated output.POST /api/mail-support-assistant/cases/sync now also accepts additive headers_raw, headers_map, and body_text_raw, and Tools stores those fields centrally on case messages so remote operators can inspect headers plus raw/plain/HTML body variants instead of only excerpts./admin/mail-support-assistant/cases/{id} now exposes stored raw headers, parsed header maps, raw plain-text bodies, normalized plain-text bodies, and HTML bodies under the case-message details view./firewall/live, separate from the older generic firewall table editor, where operators can manage children, their IP addresses, host block lists, resolved block targets, and live rules from one place./api/firewall/* surface for this feature, including child listing/update, child IP management, host block list management, live rule management, quick child block/unblock actions, and a sync endpoint for firewall regeneration.GET /api/firewall/children can now also include additive selected_host_block_list_ids, and Tools now accepts PUT /api/firewall/children/{child}/host-block-lists so clients can save that child-centric selective block selection directly.target_scope plus child_ip_address_ids[] for those flows.GET /api/firewall/children and GET /api/firewall/live-child-rules can now return additive target metadata such as selected_host_block_list_target_scope, selected_host_block_list_child_ip_address_ids, target_child_ip_address_ids, target_ip_addresses, and target_scope_summary so clients can render the exact per-IP scope safely.schedule_key mappings, and a global scheduled writes enabled/disabled switch so operators can pause schedule writes without deleting the stored rows./firewall/live.child, child_ip_addresses, host_block_lists, host_block_list_addresses, live_child_rules) instead of trying to overload the legacy firewall rule tables.live_child_rule_ip_targets, child_schedules, child_schedule_ip_targets, and child_schedule_settings, which let one live rule affect all child IPs or only a chosen subset and let recurring full off / full on rows live in the database instead of only in handwritten system crontab files.fwmaker now reads that live-control state and generates a dedicated child_live chain in the FORWARD path, so strong child blocks and selective host-list blocks can become actual iptables output instead of just database records.fwmaker now uses the extracted helper firewall_pdo.php instead of the older TorneDB dependency, its legacy template placeholders were normalized for newer PHP runtimes, the active bitcom generation path was removed, runfwmaker now falls back across installed PHP binaries instead of hard-failing only on missing php7.2, and the wrapper now suppresses stale CLI mapi.so startup warnings by using an empty PHP scan dir when pdo_mysql is still available./dnsbl/statistics, where visitors/operators can see the DNSBL API counters together with the active whitelist rows loaded from DNSBL_V5.ipwhitelist.is_local_network are called out as blacklist-exempt local-network cleanup entries rather than ordinary test-only whitelist rows./admin/dnsbl/engine-settings now also shows the same whitelist/local-network overview and includes a dedicated Purge local networks now action that removes currently listed blacklist owners whose decoded IP matches those active local-network IP/CIDR rows.GET /api/dnsbl/stats, which returns API-readable counters for /api/dnsbl/* traffic plus database-backed logical DNSBL add/delete/update outcomes./docs/dns-api) was rewritten to remove broken markdown/example blocks, align the examples with the live /api/dns/* endpoints, and explain the current auth, cache, search, and record-write flows more clearly./docs/dnsbl-api) now includes a clearer quick-start section plus practical token, inspection, add/update, delete, and dry-run guidance for current ToolsAPI integrations.RESEND wrapper mail without auto-processing restored maildnsbl-engine delist/reply runs now also scan a dedicated RESEND spool before normal reply handling and unpack attached/original .eml mail such as ForwardedMessage.eml, so the outer SpamAssassin/Thunderbird wrapper text no longer becomes the thing operators keep seeing/retrying.RESEND is now unpack-only: those restored/original mails are left in the RESEND spool and are not fed into the same delist/reply run automatically.RESEND is a Maildir path and the runner scans cur/, restored mail is now written back into sibling new/ so mail clients can show the unpacked original as a separate message.RESEND/__attachments/ for operator inspection, while the successfully unpacked wrapper itself is cleaned away afterward.FORUM bounce segment still runs together with delist/reply handling, so forum-recipient bounce processing remains part of the same operator pass.POST /api/mail-support-assistant/cases/sync now accepts additive full-body fields (body_text, body_text_reply_aware, body_html, reply_body_text, reply_body_html), and the Tools admin case page now shows that centrally stored content instead of relying only on short excerpts.MAIL_ASSISTANT_TOOLS_SSL_VERIFY, MAIL_ASSISTANT_TOOLS_CA_BUNDLE, MAIL_ASSISTANT_SMTP_SSL_VERIFY, MAIL_ASSISTANT_SMTP_CA_FILE), which helps unblock WSL/self-signed certificate setups when local CA trust is broken./feed now links directly to support for questions, complaints, and correctionssupport@tornevall.net, so feed-related questions, complaints, and correction requests can go straight into the support flow./admin/mail-support-assistant instead of only in one local latest-run summary file.GET /api/mail-support-assistant/cases plus POST /api/mail-support-assistant/cases/sync so the standalone client can fetch recent stored cases and upsert one thread snapshot back into Tools.MAIL_ASSISTANT_UNANSWERED_REPORT_ENABLED is enabled.dnsbl-engine now has a dedicated FORUM spool segment for bounce / mailer-daemon mail that is separate from ordinary DNSBL add/delist handling, similar to the earlier dedicated DMARC segment.POST /api/dnsbl/forum/bounce, where rejected recipient addresses from those bounce mails can be matched against vBulletin users and moved into a configurable bounced-email forum group as a secondary group.GET /api/dnsbl/engine-settings, which returns the normalized runtime settings used by dnsbl-engine, including the forum-bounce enable flag and group configuration.MAIL_ASSISTANT_QUOTA_ALERT_EMAIL is configured.runner_already_active conflict together with lock-holder metadata, which makes operator troubleshooting easier when a previous run is still active.cron-run.sh now also keeps its own PID-aware shell lock with stale-lock cleanup, so overlapping cron invocations can be rejected before the PHP runner even starts.[Ärende MSA-ABC12345]), and later replies in the same conversation reuse that same tag instead of appending a fresh issue id every time.**bold** or - list items.error_reason when an upstream/provider request fails.POST /api/ai/socialgpt/respond now safely normalizes structured upstream OpenAI/provider error payloads into plain error text before retry/fallback handling runs.Array to string conversion failures when the provider returns nested JSON error objects instead of a flat string, while keeping the existing fallback retry flow intact./admin/mail-support-assistant now exposes a stricter mailbox-level last unmatched fallback with an explicit enable checkbox plus dedicated allow-condition and final-instructions text fields.advanced_row_rule vs mailbox_final_fallback) in their request context so upstream SocialGPT/OpenAI audit logs are easier to map back to the real unmatched fallback path./dns/editor/{zone} now closes immediately again after a successful record update instead of sometimes leaving the expanded edit row visible.source="from_database_stale") so the editor can show something useful right away instead of showing only the AXFR spinner.inet_pton()-based IP normalization runs./dns/editor/tornevall.net from failing with inet_pton(): Argument #1 ($ip) must not contain any null bytes when malformed legacy/cache data is encountered.openai.com now restores the earlier behavior and adds both *@openai.com and *@*.openai.com.openai.com wildcard row clears the companion row too./admin/openai now uses a safer script-stack rendering path for the model-catalog refresh logic, which prevents the admin page from falling over on the latest OpenAI dashboard updates and keeps the page renderable again./register now includes an optional Where did you hear about us? field./users and /users/{user}/edit alongside the registration IP./admin/security/api-ip-whitelist has been restyled to match the rest of the Tools admin UI and now includes inline edit forms instead of the previously broken edit button./admin/dnsbl/engine-settings now includes a hygiene purge that removes any reverse-owner DNSBL / FraudBL entries that point to private IPv4 ranges in 10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16.422 with reason="private_ipv4_not_allowed_in_dnsbl".Unknown ajax action..storage/app/browser-automation/.php artisan browser-automation:run <key>, or schedule it through the existing DB-backed scheduled-jobs system with App\Jobs\Handlers\BrowserAutomationScheduledJobHandler.jobs:run every minute, so cron-based scheduled jobs can run automatically from the normal schedule:run setup instead of depending only on opportunistic web traffic./admin/browser-automation/create is now styled as a more guided admin form with clearer runtime sections, a built-in smoke-test example, and sidebar testing guidance for first-run verification.challenges.cloudflare.com is unstable, while keeping comment and account-registration protection unchanged.In-Reply-To / References.generic_no_match_rules[] row first instead of always restarting from row 1, which helps shorter repository/API follow-ups stay on the same answer path.reply_message_id in local state, which improves continuity when later Gmail/Outlook follow-ups reference the assistant's sent mail instead of the original inbound message.In-Reply-To / References, the standalone runner can now still try a normalized subject + same participants fallback before treating the mail as a fresh no-match conversation.thread_key, in_reply_to, and references[], which makes reply-chain debugging much clearer when mail unexpectedly falls through to no-match.From:-style body line when that structure lives inside the message body itself.POST /api/dnsbl/dmarc/report no longer accepts anonymous/internal uploads, admin-session fallback, or ordinary Tools API-key passthrough.can_add=true).provider=tornevall_dnsbl) and active admin-owned Tools API keys are now again accepted for DMARC upload through the same X-Dnsbl-Token / dnsbl_token transport, matching the rest of the DNSBL write auth model.401, while active DNSBL tokens without add scope now fail with 403 reason="insufficient_dnsbl_scope".projects/dnsbl-engine now keeps DMARC spool files in place when upload is denied, and DMARC-only runs now validate that the configured DNSBL token really has add permission before continuing.message/rfc822 mails that carry the real DMARC report as a nested ZIP attachment, which fixes false invalid_dmarc_report failures for those inbound mail wrappers.payload_hash; the endpoint now returns the already stored report with duplicate: true instead of surfacing a raw SQL duplicate-entry failure.Sender Domain, Sender IP Address, SPF Alignment, DKIM Alignment, and DMARC Results plus copied headers, so those reports can land in the same admin review queue even when aggregate XML is absent.Aggregate XML vs Forensic notice) to make triage easier for operators./admin/mail-support-assistant now removes unmatched fallback IF rows through AJAX without forcing a full page reload./admin/mail-support-assistant, a valid personal Tools token, mailbox configuration in Tools, reachable Tools base URL, and a configured outbound transport.POST /api/dnsbl/records/delete and delete-items inside POST /api/dnsbl/records/bulk now enrich the dedicated DNSBL removal audit trail with caller/source metadata such as source_type, source_name, site URL/host, page URL, request Origin, Referer, and user-agent when available.DNSBL_REMOVAL_AUDIT_EMAIL recipient list.reason="already_not_listed", forced_success=true) instead of a hard invalid_dnsbl_publication failure, which makes delist cleanup idempotent for callers such as dnsbl-engine.POST /api/dnsbl/dmarc/report now accepts DMARC XML / gzip / ZIP payloads for review when the caller presents an active DNSBL token, an admin session, or an active admin-owned Tools API key through the DNSBL-token transport./admin/dnsbl/dmarc instead of treating those reports like ordinary blacklist mail.spam (16) or spam + fraud (84) and also mark rows ignored when no publication should happen.projects/dnsbl-engine/run now also scans a dedicated DMARC spool folder, uploads those payloads to the new Tools endpoint, and sends a support reminder mail stamped with X-Tornevall-Mail-Assistant: sent whenever DMARC files are present.projects/dnsbl-engine/run now also supports dmarc / --dmarc-only, while projects/dnsbl-engine/start.sh dmarc and projects/dnsbl-engine/start-dmarc.sh provide a separate DMARC-only runtime path with its own PID lock.Fraud / Spam mail-engine passes now clean up obvious bounce / DSN / mailer-daemon messages instead of blacklisting the relay hops or sender IPs found inside those reports.Delivery Status Notification, Mail Delivery Subsystem, mailer-daemon, Reporting-MTA, Final-Recipient, Action: failed, and Diagnostic-Code.Auto-Submitted: auto-generated, Exchange / Office 365 NDRs, Gmail bounces, and qmail / exim / postfix daemon-style reports.runspam spam now defaults to a capped per-pass batch sizeprojects/dnsbl-engine/runspam spam now forwards a default limit of 1000 messages per run into the normal blacklist/add pass instead of leaving the batch size unlimited.runspam spam 500 when operators want a different cap.X-Tornevall-Mail-Assistant: sent.sort_order, which matches the earlier fallthrough behavior for ordinary strict-AI rejections.generic_ai_decision.evaluated_no_match_rules[], so operators can see exactly which unmatched rows were evaluated before the message was answered or left unread./admin/mail-support-assistant mailbox defaults now include spam_score_reply_threshold for reply suppression.GET /api/mail-support-assistant/config now returns additive mailbox default defaults.spam_score_reply_threshold.X-Spam-Score as an extra score source when X-Spam-Status score metadata is missing.Best regards plus Regards in the same outgoing support reply.if or instruction is empty, because those rows can never pass strict no-match triage.GET /api/mail-support-assistant/config now filters defaults.generic_no_match_rules[] to only include valid rows (non-empty if + instruction).\\Seen again when the IMAP server supports that operation, instead of only assuming the mail stayed unread.message-state.json is no longer part of unread skip/dedupe logic, but it can now still retain lightweight thread excerpts so later AI replies get more realistic conversation continuity.php run --include-history./admin/mail-support-assistant now supports mailbox-level add-row unmatched fallback rules instead of forcing one single generic IF/instruction pair.GET /api/mail-support-assistant/config now returns additive defaults.generic_no_match_rules[] with ordered row objects (id, sort_order, is_active, if, instruction, footer, ai_model, ai_reasoning_effort).defaults.generic_no_match_if, defaults.generic_no_match_instruction, and defaults.generic_no_match_footer are still returned and now map to the first active unmatched row when available./admin/mail-support-assistant now lets admins configure two separate mailbox-level no-match AI fields: an If... condition describing which otherwise unmatched mail may be answered at all, and separate Instructions... describing how to answer when that condition is met.GET /api/mail-support-assistant/config now also returns additive defaults.generic_no_match_if for each mailbox.public/tornevall-tools-mail-assistant runner now mirrors live log lines to stdout during CLI/manual runs, so bash cron-run.sh shows progress while the mailbox pass is still executing instead of only printing the final JSON block.message_results[] diagnostics, making it easier to see whether each unread message was handled, skipped, state-skipped, warned, or failed.previous_reply_recorded_unread to avoid duplicate replies.markSeen, move, or delete) fails afterwards, the runner now records an explicit warning reason instead of silently looking fully handled.to / cc / bcc recipients more defensively before SMTP or Tools relay transport is used, which improves BCC forwarding when addresses arrive with display-name formatting.MAIL_ASSISTANT_DEFAULT_BCC from its local .env.projects/dnsbl-engine/run worker now forces immediate CLI flushing and prints timestamped progress markers for scan, analyze, lookup/write, cleanup, final whitelist sweep, and operational-report delivery, which makes cron/background runs easier to debug while they are still executing.check-ip phase instead of staying quiet until the entire lookup set finishes.o4, and that fallback retry intentionally omits reasoning_effort metadata so it behaves as a plain non-reasoning fallback path..eml header dumps, and malformed embedded header runs are less likely to drown out the real original request.GET /api/mail-support-assistant/config now also returns additive user.ai_daily_budget metadata so operators can inspect the effective AI token cap/remaining budget used by Mail Support Assistant's SocialGPT-backed reply path.sort_order), and records both the selected rule and all other matching candidates in diagnostics.POST /api/ai/socialgpt/respond, /api/social-media-tools/extension/*, and /api/mail-support-assistant/* now use user-aware throttling instead of the older low fixed per-minute caps./admin/menstrual-tracking/{user} to edit another user's menstrual profile and cycle rows without logging in as that user./users/{id}/edit into that user's tracking view.429 / Too Many Attempts are now retried automatically by the standalone Mail Support Assistant before it gives up on the AI reply path.Thank you for your message. We have reviewed it. when AI fails; the reply is now aborted and logged instead of pretending the issue was handled.multipart/alternative, keeping the original plain-text body while also adding a styled HTML version for richer-looking support mail.POST /api/mail-support-assistant/send-reply now also accepts additive body_html, so Tools relay delivery can preserve the formatted reply body when the standalone runtime falls back to relay mode.responder_name, persona_profile, custom_instruction, selected ai_model, and additive ai_reasoning_effort to Tools as explicit one-request overrides./admin/mail-support-assistant now uses dropdowns for Mail Support Assistant AI model selection and per-rule/per-mailbox reasoning effort instead of relying on free-text model input for those AI controls.dnsbl.tornevall.org + opm.tornevall.org as well as bl.fraudbl.org, restoring the older combined DNSBL+FraudBL behavior for phishing/fraud flags.bl.fraudbl.org + ecom.fraudbl.org without being mirrored into the ordinary DNSBL zones.socket_recvfrom(): Interrupted system call before the operation is treated as failed.projects/tornevall-tools-mail-assistant no longer marks mail as read when it was skipped only because no rule matched or the generic unmatched-mail fallback was disabled, unanswerable, or failed.mark_seen_on_skip is now effectively limited to deliberate heuristic skips such as high-score SpamAssassin junk, which makes mailbox misconfiguration easier to notice and retry./admin/mail-support-assistant now includes mailbox-level settings for a generic AI fallback reply when an incoming mail does not match any explicit rule.GET /api/mail-support-assistant/config now includes defaults.generic_no_match_ai_enabled, defaults.generic_no_match_ai_model, defaults.generic_no_match_instruction, and defaults.generic_no_match_footer.projects/tornevall-tools-mail-assistant now supports selectable outgoing transports: local PHP mail(), custom MTA command, or direct Tools relay mode.smtp, reducing postdrop/local sendmail dependency issues in cron environments.POST /api/mail-support-assistant/send-reply.provider_mail_support_assistant_mailer) plus a new permission gate (mail-support-assistant.relay)./admin/mail-support-assistant now also has relay-token rotation UI and optional one-click permission grant for the selected token owner.Array to string conversion warnings when Tools returns structured error arrays.delete_min_cidr_prefix, so non-admin token owners can be limited to smaller delete ranges such as /25../32 instead of receiving blanket CIDR access.can_cidr_delete, and CIDR delete attempts now fail explicitly with delete_cidr_not_allowed or delete_cidr_prefix_too_broad when the requested range is outside the delegated floor.delete_limit_per_day, delete_cidr_limit, delete_throttle_limit, and delete_throttle_window_seconds.delete_daily_limit_exceeded (429), delete_throttle_exceeded (429), and delete_cidr_limit_exceeded (422).delete_guardrails metadata so clients can show effective limits before delist attempts.dnsbl_api_tokens columns have not been migrated yet, returning default/no-guardrail metadata instead of crashing the delete endpoint.projects/dnsbl-engine/run now suppresses the summary report mail entirely when a run did not touch any actual address activity, which avoids "nothing happened" report spam from no-candidate/no-op spool passes.messages_read_skipped / mailbox read_skipped counters so already-read IMAP mail does not look like no_matching_rule noise.is_seen, and messages already marked read at ingest are skipped without being recorded as new ignored no-match state rows.excluded_read_records and raw_count metadata to make state diagnostics clearer when operators compare visible rows against raw storage.storage/state/message-state.json as a hard dedupe gate for unread mail.messages_previously_recorded_unread when an unread thread was found in local history and deliberately re-evaluated.projects/dnsbl-engine worker now also cleans up spool files when an IP is skipped because the live DNSBL already has the same or a higher bitmask, instead of leaving those files pending forever..env values such as DB_DNSBL_DATABASE, DB_DNSBL_FIREWALL_DATABASE, DB_DNSBL_CONNECTION, and DB_SA_CONNECTION drive the live whitelist source setup.SPAM_REPORTS_FROM and SPAM_REPORTS_TO are configured, add/update runs and delist/reply runs now send an operational summary mail with stats, whitelist-match details, and final whitelist-sweep output.sender_matches_whitelist_from is auto-overridden and the mail continues through normal Fraud/Spam handling.projects/dnsbl-engine now also has a narrow --force-sender-whitelist-holds operator flag that bypasses only sender_matches_whitelist_from manual holds, without turning off the other low-score / safe-test protections.gpt-5.4 with medium reasoning effort first, and if that pass comes back empty the worker immediately reruns the answer through the fallback model (gpt-4o-mini) instead of sending a blank AI section.support@tornevall.net appears in the thread, which allows real customer follow-up replies to earlier support mails to be answered normally.projects/tornevall-tools-mail-assistant runner now handles reply chains better: Re:/Fwd:/Sv: subject prefixes are stripped before rule matching, quoted historical mail blocks are removed before body matching and AI summaries, and outgoing replies now preserve In-Reply-To / References headers.Message-Id values plus a stable fallback local message key when the header is missing, so skipped/handled mail appears correctly in local message-state summaries.scanned but handled=0 runs much easier to diagnose.IPv6: no longer leak a bogus leading 6: into the processed address list.DNSBL_AI_RESPONDER_NAME, DNSBL_AI_PERSONA_PROFILE, DNSBL_AI_CUSTOM_INSTRUCTION) for operators who want reply-specific AI tone independent of saved SocialGPT settings.gpt-4o* model prefixes by default, so gpt-4o/gpt-4o-mini fallback runs can keep the same reasoning behavior when configured.projects/tornevall-tools-mail-assistant AI path now mirrors the same behavior profile: sanitized message summaries, configurable reasoning effort, and one primary-to-fallback retry (gpt-5.4 -> gpt-4o-mini).matched_entries evidence layer from SQL retrieval into the final AI context instead of using the SQL match only as a hidden filter step.title, description, content excerpts, publishby, feed_title, and link data all the way into the answer stage.? channel replies, and escalates question abuseinvite / op / kick inference helper that its channel-mention flow calls, fixing the runtime fatal about ircwatchInferStructuredChannelActionFromMessage() being undefined.? can now trigger a short bot answer even without a direct bot mention.?-bait can now produce warnings, while the ? reply path itself no longer auto-kicks anyone.o access, not only owner/master-style flags./admin/mail-support-assistant configuration surface now uses clearer card groupings and better-formatted mailbox/rule sections so the page is easier to scan and edit.åäö characters.projects/tornevall-tools-mail-assistant/ instead of the earlier temporary projects/mail-support-assistant/ path.projects/dnsbl-engine worker now keeps queued spool files until the Tools DNSBL bulk write has actually accepted the corresponding add/update operations.op, invite, and kick through the same structured channel-action layer that already handled topic-style actions, which keeps PM/manual execution more consistent.Op and Invite actions in addition to Topic, Mode, and Kick./admin/mail-support-assistant.GET /api/mail-support-assistant/config, which lets the standalone PHP client fetch its mailbox/rule configuration by bearer token instead of keeping a separate local database.projects/tornevall-tools-mail-assistant/ with its own README.md, CHANGELOG.md, AGENTS.md, env-driven mini web UI, and CLI/cron runner skeleton.is_ai consistent while provider_openai stays excludedis_ai flag is now normalized more consistently whenever API keys are created or updated from the Tools key UI.tools_ai_bearer tokens still force is_ai=1.provider_openai secret is now explicitly excluded from is_ai, because it is the provider secret used towards OpenAI, not a client token used towards Tools.tools_ai_bearer row.tools_ai_bearer provider key.provider_ircwatch) while still authenticating against Tools-hosted AI endpoints.provider_openai access or admin bypass; the AI-capable token only proves client identity and user ownership.op #channel and ge mig op på kanal #channel, which send MODE #channel +o <nick> when the requester is authorized for that channel.kick <nick> <#channel> [reason] for a direct kick with their own reason.aikick <nick> <#channel> <motivation> lets IRCWatch ask Tools/OpenAI for a short kick reason before sending the kick.manifest.json as the Chrome-first development/test manifest.projects/socialgpt.sh, which stages browser-specific archives under projects/socialgpt-chrome/dist/ instead of zipping the raw source folder directly.browser_specific_settings.gecko patch so the repository manifest can stay Chrome-oriented.client_platform value (chrome_extension, edge_extension, opera_extension, or firefox_extension) instead of always claiming Chrome./feed shows one edition per period again, and feed-admin can now purge extra cached editions/feed overview now shows only the single selected public edition for each category/site period (daily, weekly, monthly, yearly) instead of rendering multiple cached editions inline./feed/cards/{categorySlug}?show_all=1./out/{contentId} redirects/out/{contentId} redirect instead of exposing the external URL directly./24-/32 ranges in small local batches, shows live progress while scanning, and keeps a visible hit list of listed IPs found in the block.3.1.0 line instead of inventing a separate version bump.POST /api/dnsbl/records/delete and delete items inside POST /api/dnsbl/records/bulk now emit dedicated DNSBL removal audit entries in the backend, separate from the broader generic DNSBL API request log.success, denied, failed, or validation-related rejection).ModelCatalogService::getAvailableModels() and makes the configured answer model follow the current catalog contract again.Undefined variable $feedIds failure that could abort some questions before the answer stage./feed, the site-specific question panel, and live AJAX-updated answer boxes, so markdown links/paragraphs are no longer shown as plain raw text on those newer surfaces.POST /api/dnsbl/records/delete and delete items inside POST /api/dnsbl/records/bulk now derive the actual listed owner names from a live DNS inspection of the submitted IP before any low-level DNS delete is attempted.publication_type / bitmask is no longer trusted as the source of truth for delete scope; the DNSBL endpoint now decides which of dnsbl.tornevall.org, opm.tornevall.org, bl.fraudbl.org, and ecom.fraudbl.org must be deleted.fraudbl.org / tornevall.org) while still deleting the concrete child-zone owner names, fixing NOTAUTH delist failures where BIND rejected bl.fraudbl.org or opm.tornevall.org as the update zone./settings/integrations/microsoft-todo now includes a platform-app diagnostics panel showing whether the shared Microsoft Entra / Graph app is available, which required fields are still missing, and the effective/recommended callback URL for the current host.GET /api/microsoft-todo/status now returns additive non-secret platform_app diagnostics so authenticated clients can distinguish “not connected” from “host app not configured yet.”/feed/entry can now switch to side-by-side diff view/feed/entry/{contentId} now has a small diff-view toggle so operators can switch between the existing highlighted inline diff and a side-by-side before/after comparison for each adjacent revision pair.dns_zone_settings row yet.Attempt to read property "last_invalidated_at" on null error on first load for zones that have never had per-zone cache settings saved./feed/cards/{category} page now respects the show_all=1 query flag from the overview page, so the linked page displays every stored analysis variant for each period instead of silently collapsing back to one selected variant.show_all mode when changing pagination or history settings, and the period header clearly states how many variants are being shown.bl.fraudbl.org and ecom.fraudbl.org use the fraudbl.org key configuration, while dnsbl.tornevall.org and opm.tornevall.org use the tornevall.org key configuration.bl.fraudbl.org.conf on systems that only store fraudbl.org.conf./feed now include the stored bucket label, so older cached variants are easier to identify before deleting them.daily: current day,weekly: current ISO week (Monday start),monthly: current calendar month,yearly: current calendar year.period_bucket) when rendering feed-admin cards, so stale default variants from older buckets no longer mask the latest period output.Week 16 (2026), Month 2026-04, Year 2026) together with generation timestamp.current ISO week, current month to date) instead of rolling last 7/30 days wording./api/dnsbl/* routesPOST /api/dnsbl/* endpoints (including check-ip, records/add, records/delete, records/update, records/bulk) are now excluded from CSRF verification in VerifyCsrfToken.web middleware stack (and therefore sessions) is still active on the DNSBL routes./api/dnsbl/check-ip and /api/dnsbl/records/* auth failures now distinguish between:
reason="wrong_token_type"), andreason="inactive_admin_api_key").GET /api/dnsbl/token/info, so clients can see whether a supplied token was recognized but belongs to the wrong auth model./admin/virtualbox for managing VirtualBox hosts through vboxwebsrv.virtualbox.manage can register their own VirtualBox server endpoints (host, port, username, password) and keep multiple server segments in one panel.start, stop, restart, delete, plus core settings update (memory, cpus, vram, boot1).virtualbox_servers table) and a dedicated permission migration for virtualbox.manage./services and new EN/SV docs page (/docs/en/virtualbox-manager, /docs/sv/virtualbox-manager).VirtualBox Manager now reads the actual WSDL function list first and prefers VirtualBox-specific SOAP operations such as IWebsessionManager_logon before falling back to generic guesses like logon.
VirtualBox Manager now retries login through multiple SOAP client candidates when the WSDL client reaches the server but fails with a namespace/method fault such as Method 'ns1:logon' not implemented: method name or namespace not recognized, including both known VirtualBox SOAP URIs.
Failed VirtualBox login diagnostics now keep the effective endpoint, WSDL URL, selected SOAP mode, selected SOAP URI, available WSDL functions, and the attempted login sequence visible in the raw debug payload.
Most VirtualBox admin operations on /admin/virtualbox now run through AJAX, so routine actions no longer require full page postback/redirect cycles.
Added direct VM provisioning from each server segment (Create new VM) to cover practical setup even when unattended install is not used.
New VM defaults are now aligned for faster setup: bridged network mode, 1024 MB memory, and 25 GB disk.
The create form can now check server-registered installable DVD/ISO media and offer those paths as quick suggestions.
If ISO inventory lookup is unavailable on a server, VM creation still works with manual ISO path (or no ISO) so setup can continue.
VBoxManage command flags (-H, -p, -u, -w) that produced Oracle CLI usage dumps such as Invalid command '-H'.VBoxManage commands only.host, port, username, and encrypted password from each server card are now used to run remote VBoxManage --nologo ... commands.22 for SSH, not legacy 18083).sshpass exit code 5) reports credential guidance,255) reports host/port/firewall guidance./admin/virtualbox UI/help text now matches the runtime: local execution for localhost targets, SSH execution for remote targets.POST /api/ai/socialgpt/respond now accepts additive client metadata fields: client_name, client_version, and client_platform.client object, which makes it easier to identify which extension/app build made the request..env values, passwords, tokens, API keys, and similar secrets.inet_pton() runs.inet_pton(): Argument #1 ($ip) must not contain any null bytes when malformed/pasted search values reached /api/dns/zones/{zone}/search.support@tornevall.net (or the configured SMS activity recipient) whenever an inbound SMS is stored or an outbound SMS is queued.POST /api/dnsbl/check-ip, a live DNS inspection endpoint that returns the currently listed DNSBL/FraudBL publication families for one IP plus the delist candidates that the authenticated token/session can act on.dnsbl, fraudbl, commerce) instead of relying only on the first local resolver impression.sabah survive even when the AI proposes broader phrasing.period_bucket from the active period reference date instead of the rolling window start date.GET /api/dnsbl/token/info, and the page is only accepted when the configured token currently has delete / delist access.[dnsbl_removal_form] or [tornevall_dnsbl_removal_form], and those shortcode forms now only expose DNSBL operations that the configured token is allowed to perform.Tara Sabah or Tara Saleh, instead of odd variants like tara sabah artiklar, tara sabah analys, or tara saleh källor./feed now reflects those cleaned phrases too, so the visible hints better match terms that can actually occur in article titles, descriptions, content, or bylines./feed now shows a separate readonly search keywords used box under the latest answer, alongside the existing extra-terms helper./feed/user-questions now surfaces those literal retrieval keywords inside the How this answer was analyzed box, so users can compare the exact searched phrases with any broader extra terms./api/rss/update now guards its DOM helper calls against null values so partially matching or malformed elements rule sets no longer crash the whole inbound conversion batch with method_exists(..., null) type errors./users, /users/{id}/edit, /keys/mine, and /admin/openai now handle a missing openai_access_requests table gracefully instead of throwing a SQL exception.OpenAiAccessRequest model is now explicitly bound to the real openai_access_requests table name, which also fixes environments where Laravel would otherwise infer the wrong open_ai_access_requests name.har, du, något, spännande, with, about, and similar words is now filtered out before SQL-backed retrieval, even if those words appear in the original question text.GET /api/dnsbl/token/info no longer rejects non-empty token strings purely because they fail a local length/format check.401 when no token is supplied, but otherwise now performs a real lookup before answering.X-Dnsbl-Token flow, with full effective permission fields in the success payload instead of the old "not a dedicated write token" wording.Returning users series in the same graph./admin/social-media-tools can now open that dashboard directly from My Profile instead of having to discover it only through My API Keys./admin/security/online-users now infers authenticated web users from the session payload and recent tracked page visits when the database sessions.user_id field is empty, so logged-in users no longer look like guests just because the default auth guard is api./rss now uses one consistent explicit probe User-Agent instead of mixing a self-identifying Tools string with generic Mozilla/5.0 requests./keys/mine includes a request form, /admin/openai includes the review queue, and non-admin AI usage now requires real approved access instead of only inheriting the existence of a daily budget cap./feed, /feed/c/{categorySlug}, /feed/cards/{categorySlug}, /rss, and /feed-admin now rewrites accidentally stored localhost/loopback links to the current public host before rendering on screen./feed category overview now refreshes long-analysis overflow detection when a collapsed category is opened, restoring the Read more button there.real_url/url, so /out/{contentId} no longer redirects into path-only links when a source only stored /foo/bar.publishby values (for example numeric or legacy IDs) keyed by urlid + publishby, so the same raw value can safely mean different people in different feeds.publishby values to a real display name./api/rss/feed/{site} readers.article, latest, feed, discussion, nyheter, and similar broad labels are now filtered out before database narrowing.sc4a-insights companion-module note.GET /api/social-media-tools/extension/validate-token.[1], [2] numbering unless the user opts in.tn-mark-2), richer element descriptors, and optional broader extraction modes such as one parent up or one parent up + direct child scan.× control now works again after the draggable-header change.Open Toolbox and Verify fact appear more reliably after short direct selections and double-click selection gestures.about:blank child frames) where Chrome allows it.GET /api/rss/urls now returns additive scraper-idle metadata (idle) when no URLs are currently due for the requesting agent.wait_seconds, wait_minutes, next_poll_at, and the next expected URL identity so scraper workers can see when work is likely to resume.projects/scraper/scrape.php runner now writes timestamped status lines and tries to persist them to SCRAPE_LOG_FILE or /var/log/tools-scraper/scrape.log when writable.docs/en/rsswatch.md that caused metadata to render as visible text in docs output.RssWatch-2.0/<hostname> in scraper runners with crawler-style RSSWatchBot strings including a docs URL.agent_id=<hostname> in API calls./feed/cards/{categorySlug} now renders AI analyses first and then the category article stream below (instead of analyses-only focus).daily, weekly, monthly, and yearly when available.rss.content.publishby to reduce cross-author mixing.link field) for recent entries and version history articles.[Title](url) if the article URL is available in the context./feed now renders markdown links as clickable hyperlinks./feed now shows a small notice below the description reminding users that answers are based on available indexed content, that some sites may have limited history, and that answers are not always complete or fully accurate.ask_error_required, ask_error_captcha, ask_error_generic, site_analytics_title (were falling back to English).ask_error_required, ask_error_captcha, ask_error_generic.ask_asking, ask_error_required, ask_error_captcha, ask_error_generic.ask_error_required, ask_error_captcha, ask_error_generic, site_analytics_title.GET /api/dnsbl/token/info endpoint lets any client check the permissions of a configured DNSBL token without needing a logged-in session.X-Dnsbl-Token header or ?dnsbl_token= query parameter.sanitize_option callback was calling update_option which re-triggered the same filter, producing an infinite recursion. Added a static re-entrancy guard to prevent this.POST /api/dnsbl/records/add, /delete, /update, and /bulk now accept optional dry_run.dry_run and dry_run_accepted markers to confirm that the backend accepted simulation mode.[dnsbl_removal_form]) with HTML/AJAX submission through WordPress backend proxy./feed/user-questions now shows a compact analysis box per completed row./feed categories – compact cards page and per-period analysis cap/feed/cards/{categorySlug} for a compact, card-style AI analysis view./feed/c/{categorySlug} remains unchanged and now includes a shortcut link to the cards page./feed now show a max of 3 items per period (daily/weekly/monthly/yearly), with a direct "Show all" handoff to the cards page.publishby/feed free-text search now includes content.publishby in fallback LIKE matching and ranking, improving person-name lookups.creator/author metadata in content.publishby when feeds expose it (including more feed variants where this field previously came through inconsistently).content.publishby when missing, so the byline can be persisted instead of being dropped.link + title + description + content are unchanged, a new row is not created just because publishby appeared, disappeared, or changed./rss row editing now uses a dropdown with existing categories for faster reassignment.Custom... option is available when you need a new category value; custom values still save inline via AJAX./feed-admin now include the same quick category dropdown/custom input, so category changes can be done directly from Feed Admin without opening /rss./rss add-feed form now checks existing feed rows before insert.URL or Real URL already matches an existing feed (including URL ↔ Real URL cross-matches), the insert is blocked and the matching row details are shown in a warning.<time> text now prefers ISO timestamps<time class="tn-date"> node whenever an ISO value exists.utf8_decode(...) on rendered row values before insert./feed – category cards now default to collapsed and support saved accent colors/feed now start collapsed by default when no previous preference exists.wp-json is protected401 and 403 responses from /wp-json/wp/v2/posts as unavailable WordPress REST endpoints.<link rel="alternate">, /feed, /rss.xml, etc.) instead of assuming the WordPress REST endpoint can be used.centerpartiet.se news pages before the normal RSS/XPath import step.AppRegistry.registerInitialState(...) payload dynamically from the page's bootstrap scripts and converts the item list into stable synthetic HTML.sitetype=xpath rules, which makes Centerpartiet's news listing easier to ingest with the existing importer without teaching /api/rss/update a new raw-script format.<article> nodes from the page.href/attribute issues/rss/xpath-lab now shows both a fast lab preview and a second Production / legacy scraper compatibility preview that runs the same XPath renderer used by live imports.value extractors such as href are silently skipped because the legacy renderer expects matching extractor keys in pipeline[3]./@href, /@src, /@datetime) and map them with "value"./feed and the site-specific feed question panel now switch the submit button into a visible spinner state while a question is being analyzed./feed broadened question retrieval with extra related phrases, the latest-answer panel now shows those phrases in a readonly helper box below the answer./feed/entry/{contentId} now scopes history lookups to the same feed (urlid + link) instead of scanning by link alone./feed/feed./feed while looking up matching feed entries.host_permissions narrowed to the Tools API servers only (tools.tornevall.net, tools.tornevall.com). The <all_urls> host permission moved to optional_host_permissions.<all_urls>, then registers content scripts on all sites. Disabling the toggle unregisters the scripts and removes the optional permission.activeTab when a menu item is clicked on a page without a running content script.CHROME_WEB_STORE_PRIVACY.md added to the extension source directory as the authoritative Chrome Web Store submission document.docs/en/socialgpt-chrome.md added as end-user documentation for the extension permission model, features, and storage.resetReplyTransientFieldsButKeepContext is not defined in extension runtime logs after AI responses./docs/en/privacy-policy./docs/sv/privacy-policy./docs/en and /docs/sv) with a visible legal section and direct privacy-policy links.DocsController now auto-discovers public docs slugs from language folders (docs/en/*.md, docs/sv/*.md) instead of a manually maintained allowlist.visibility: admin or access: internal) instead of a hardcoded slug denylist./feed question answering now starts with a keyword extraction pass: OpenAI derives compact search terms from the user question before full context assembly.rss.content rows by keyword matches in title/description/content and only then expands context blocks + version history.daily/weekly/monthly) and stricter caps for yearly to keep prompts bounded.all_time period option for whole-database analysis, so questions can search across the full stored RSS history instead of stopping at the current year window.strict, balanced, expansive), while admins can define the default mode used on /feed and site-specific feed question panels.strict fulltext -> relaxed fulltext -> LIKE fallback) and merges ranked hits, which improves narrow-topic questions that previously returned too few matches./rss) – add form URL mapping + category selector workflowreal_url and uses the auto-detected feed endpoint as url.URL with the detected feed URL while preserving the typed source page URL in Real URL.Open Toolbox button next to Verify factEsc, matching the movable floating-button behavior used elsewhere in the extension./rss/xpath-lab now supports pasting and testing the full 5-element pipeline / elements JSON format used by sitetype=xpath feeds.VERSION_SAMPLE_LIMIT_BROAD from 4 to 6 and VERSION_SAMPLE_LIMIT_NARROW from 6 to 12.docs/en/rsswatch.md and docs/sv/rsswatch.md now include a full API Reference section covering all /api/rss/* endpoints with request/response schemas, auth, and parameter tables.elements / Pipeline JSON format with a complete annotated example of the Socialdemokraterna-style XPath rule set.version_history.articles JSON structure returned to OpenAI./feed/c/{categorySlug})/feed/c/{categorySlug}./feed now include a Share button (next to admin actions) that opens the matching category page directly./api/rss/feed/{categorySlug})./rss/xpath-lab (permission: rss) for debugging and building XPath rules from pasted HTML snippets.xpath_query) with immediate match previews (node path, tag, text, and snippet).elements rules./rss to the new lab and a helper link in the auto-detect HTML-context section.version_history.articles[*].versions[*] now includes description and conditional content excerpts (when content is present), not title-only rows.My To Do, Whisper, and Whisper Admin shortcuts from the global navbar./services (permission-aware cards)./feed/feed default feeds per page.per_page query parameter is provided./feed?per_page=...) and values remain clamped to safe bounds./feed/entry/{contentId} for noisy posts:
/feed/{urlid} question panels.context_type, analysis_note, period_metadata – scope metadatafeeds – list of matching feeds with id, title, category, urlrecent_entries – period-filtered entries with version count annotationversion_history – period-independent list of all edited articles with every stored version (v1=oldest, vN=newest), title change notes between adjacent versions, and permalinkstop_terms, stats – frequency summary and scope countersversion_history.articles covers ALL recorded time regardless of the selected period (daily/weekly/monthly/yearly), so the AI can answer questions about article edits spanning any date range.version_history.articles to answer questions about edits, content changes, title evolution, and publishing history.buildContextSnapshotFresh() and version-history lookups to reduce latency/timeouts under scoped questions:
content(urlid, pubdate, contentid) for period-window entry samplingcontent(urlid, link, content_hash) for version-count groupingcontent(urlid, link, pubdate, contentid) for version-history row retrievalurls(deleted, is_hidden, category, title, urlid) (or urls(deleted, category, title, urlid) where is_hidden is absent)version_count, title_change_count, first_published, last_published, and a compact sampled versions[] list instead of every stored revision.debug_summary, domains, some sources/terms/feeds/recent entries) and then reduce version_history.articles until the JSON payload fits the configured character budget./whisper (permission:whisper.use)./admin/whisper (permission:whisper.manage) with all-user visibility and run-now trigger./api/whisper/*:
GET /statusGET /jobsPOST /jobsGET /jobs/{jobId}POST /run-now (manager/admin)whisper_transcriptions with stage/progress fields, retry metadata, transcript storage, and owner linkage.whisper:process --limit=1, every minute) plus host config/env support (WHISPER_*) for binary path, timeouts, retries, and SSL behavior./settings/integrations/microsoft-todo./api/microsoft-todo/* for status, sync, list retrieval, task retrieval, and list/task mutations.microsoft-todo:sync) that runs every 15 minutes.source=social_media_extension, SocialGPT tool slug, and feature_slug=gpt) for both list and detail views./admin/social-media-tools/audit/complete.RssController that had caused runtime failures during RSS inbound conversion./api/rss/update crashes caused by missing canQuery() and handleWordPressRestApi() methods./api/rss/feed/* and analytics feed selectors after the accidental method removal.Asking…), the re-rendered answer box labels (Latest answer, Q:, A:), success text, and error messages — instead of always showing English.en, sv, da, no, fi):
ask_error_required – shown when the question field is left empty.ask_error_captcha – shown when the captcha has not been completed.ask_success_answered – shown after a successful answer is received.ask_error_generic – shown on network/server errors.ask_asking phrase in the Norwegian (no) language entry.Daily, Weekly, Monthly, Yearly) and Read more / Show less buttons now respond to the language switcher.site_analytics_title phrase key (site-specific AI analysis summary label) to all languages.Updated TLS configuration in nginx/tools.tornevall.net.conf to remove deprecated TLSv1 and TLSv1.1.
Now only TLSv1.2 and TLSv1.3 are accepted.
Updated cipher suite to modern ECDHE/CHACHA20/AES-GCM set and set ssl_prefer_server_ciphers off (lets clients negotiate the best mutual cipher).
Updated scraper URL selection so always=0 is now agent-aware instead of globally gated by urls.lastscrape.
Each scraper agent (agent_id) now tracks per-URL claim state: when it last saw a URL and when it is next allowed to see it.
Interval checks for always=0 now use the URL's configured readinterval together with that agent-specific seen state.
always=1 now returns all non-deleted/non-noscrape RSS URLs (no interval filtering).
Added backend persistence table rs_agent_url_seen (RSS DB) for per-agent URL scheduling state.
today/day, week, month, year) when no explicit period is selected.user_selection, question_text, or admin_default) for traceability.gpt-4o-mini./feed)/feed page.navigator.languages preference on first visit, then persisted in localStorage.config/feed_phrases.php (editable as code)./rss-admin → 🌐 Public feed phrase editor)./feed)localStorage and restored on each visit, so the page opens where you left off.toggleCategory() function is now correctly wired./feed)/feed/user-questions). Free-text, max 500 characters, injected directly into the prompt. Use it to enforce plain prose, suppress bullet lists, or shift the output personality without touching the underlying prompt profile./api/rss/update)handled=1 are now purged as soon as they are unlocked (processlock=0) during update runs.inbound table lighter under sustained feed traffic.processlock=1) are not purged.processlock=1 via RSS admin (/feed-admin, alias /rss-admin) using setting key feed_admin.inbound.processlock_release_minutes.5..720 minutes) and is applied to both handled and unhandled stuck inbound lock recovery./feed)/feed)gpt-5.4.reasoning_effort=medium for reasoning-capable models (gpt-5*, o1*, o3*, o4*)./rss editor now attempts the WordPress REST API (/wp-json/wp/v2/posts?per_page=100) before any other method. If the site serves valid WP-JSON posts the real_url is set to the REST API endpoint with ?per_page=100 (to retrieve more posts than the default RSS feed) and sitetype is set to wp. Falls through to standard RSS discovery if wp-json is disabled or unavailable.<link rel="alternate"> extraction: If WordPress REST API is not available, the editor now fetches the page HTML and parses all <link rel="alternate" type="application/rss+xml|atom+xml"> tags to discover the canonical feed URL declared by the site itself — avoiding guessing.*.blogspot.* are now flagged in the auto-detect response. A warning is shown in the editor status line noting that Blogspot RSS output is minimal and that full-HTML scraping may be added in the future.<link rel="alternate">, no common feed pattern), the editor now calls OpenAI (gpt-4o-mini via the configured OpenAI provider) with a truncated snapshot of the page content and asks it to suggest a feed URL. The result is used as a fallback and clearly flagged with "🤖 AI-suggested URL – please verify" in the UI. Silently skipped if OpenAI is not configured./feeds/posts/default) added to the common feed URL probe list.extra_content added to the RSS database (migration 2026_03_28_100000_create_rss_extra_content_table).html_blob) and screenshot file references (filepath).urlid, contentid, link, content_hash, scrape_type, filepath, html_blob, scraped_at./feed)/feed (daily, weekly, monthly, yearly) so each question can choose its own analysis window without changing global admin defaults./api/rss/data)urlid and URL, improving operator visibility during batch runs.POST /api/rss/data response payload (received) so each row now also includes the feed title from urls.title.candidates, converted, and skip counters) to make zero-entry outcomes easier to diagnose.https://www.google.com/url?...) to better preserve destination links during import./users/profile where users can update their own name and email./online admin location visibility so tracked request URIs are matched more reliably against active sessions. Page-visit writes now tolerate missing optional columns during partial deployments, and online matching now falls back more safely for user/session/user-agent combinations./online so authenticated admins/users with users permission now see the detailed location-aware online view on that same URL instead of the simplified public table./rss where legacy Blade flags ($hasProtectedColumn, $hasUseProtectedColumn) could be undefined if deprecated columns are absent./feed when visiting /rss, /feed-admin, or /rss-maintenance-admin, instead of seeing login/403 pages./rss by restoring row cells for protected and useProtected, preventing is_hidden/public_hash values from appearing under the wrong headers.is_hidden on /rss from a free-text input to a checkbox, with AJAX save now posting explicit 1/0 values./admin/jobs RSS Analytics Scheduler is now editable (enabled toggles + run_at per period) and includes a manual "Run Scheduler Check Now" action.rss:generate-analytics: direct cron runs now respect per-period enabled + run_at, skip before slot time, and skip already-completed period keys; manual override remains available via --ignore-scheduler-window./mcu web route to redirect to the controller-backed MCU editor route (mcu.index) instead of rendering the editor Blade without required view data (which caused $perPage/filter variables to be undefined)./keys/mine) → "Google Account" section./auth/google/callback) handles both sign-in and linking flows.GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_REDIRECT_URI to .env (see .env.example)./admin/visit-stats now exclude RSS scraper/cron paths by default./api/rss/data, /api/rss/urls, /api/rss/update, /api/rss/feed/analytics-*, /api/rss/analytics/run.tools.tornevall.net and "GitHub / Support" link to https://github.com/Tornevall/mcu-timeline-api at the top of the MCU Timeline editor page.dns_zone_cache_records) to support very large AXFR zones with significantly faster paged reads.dns_zone_cache_ip_extras) populated for forward A/AAAA records and decoded reverse-owner entries (DNSBL/OPM style), so future CIDR workflows can use indexed IP metadata.POST /api/dns/records/add|delete|update|bulk) to synchronize cache rows after confirmed successful master DNS updates instead of invalidating full zone caches.dns_zone_settings:
cache_invalidate_enabled (default false)cache_invalidate_interval_seconds (default 259200 = every 3 days when enabled)last_invalidated_atdns:cache:invalidate (hourly scheduler hook) that only invalidates zones where policy is enabled and due./admin/jobs that shows per-period analytics run configuration (enabled, run_at) and latest scheduler state (last_run_at, status, key/reason), so operators can verify timing behavior without opening Feed Admin first.tornevall-tools-cron to run php artisan schedule:run every minute so configured analytics schedule times are actually respected by the scheduler./)/online admin view)user_id consistently and can also store Laravel session_id for more accurate session-to-location matching./feed + /feed/user-questions)/feed so the question form now reuses the shared global Turnstile key when the older feed-specific provider key is missing. The page also now blocks submit until a captcha token is present and shows a clearer temporary-unavailable notice instead of failing silently when captcha keys are not configured correctly./feed, users can now optionally focus the AI's analysis on specific categories or sites using two new multi-select dropdowns:
/feed user questions, selected focus_site_ids[] now override category focus for that question (instead of category-driven broad matching). This keeps context/query scope aligned with the explicitly selected sites and reduces oversized prompts.--force now actually forces overwrite: php artisan rss:generate-analytics now skips unchanged bucket+variant signatures by default and reuses the cached row instead of spending extra AI calls on identical snapshots.--overwrite-current as a clearer alias for --force when operators explicitly want to re-run and overwrite the current bucket row./feed-admin now includes per-period scheduler controls (daily/weekly/monthly/yearly) with enable/disable and run-time fields. A minute-level scheduler command (rss:run-scheduled-analytics) now handles due/catch-up execution from cron.[AUTO] Scheduled analytics) and always overwrite the current automatic bucket variant in place.rss:generate-analytics now prefixes output lines with date/time, making rss-analytics.log easier to audit per generation./ and /services)/ and /services now render the same landing view./menstrual-tracking)birth_date, first_period_started_on) and register cycle starts either as exact dates or month-level entries./, /services, dashboard cards, and navbar./docs/en/menstrual-tracking and /docs/sv/menstrual-tracking.PHP_SUPPRESS_DEPRECATIONS in bootstrap to mute noisy PHP deprecation notices (E_DEPRECATED, E_USER_DEPRECATED) on PHP 8.4+ when running older vendor stacks..env.example documentation for the flag.docs/cron.md for deprecation warnings in cron output./feed mini-card + /feed/user-questions)/feed page now show a truncated preview (280 chars) of each answer. If an answer is longer, a read more / read less toggle button appears, allowing inline expansion without leaving the page./feed/subscriptions) — Stricter Duplicate Filteringcontent_hash that has ever been delivered for each link within a subscription. If a new import row has the same hash as any previously reported version of that link (even if it received a new contentid due to cycling content — A→B→A), it is silently skipped and not re-reported. This prevents notification spam from feeds that repeatedly publish minor or near-identical variations.canQuery) now also checks whether the new entry's content_hash or meaningful_hash already exists in any previous row for that link, not just the most recent row. This closes the A→B→A cycling hole where version A was being reinserted into the content table even though it already existed in older history. Both content_hash and meaningful_hash are checked globally via indexed existence queries, preventing runaway version accumulation from feeds with cycling or near-identical content.projects/scraper/ondisney.php)original_url fallback always populated: Disney sitemap URLs always carry locale prefixes (e.g. en-be, nl-nl). The scraper previously only set original_url on the rare case of a URL without a locale prefix, meaning most items never had original_url set and were silently dropped by the import engine. Now the scraper falls back to a preferred English locale (en-us → en-gb → en-ca → first en-* → first available) and always sets original_url (and original_title if missing) on each item. Existing elements DB JSON for site 8 is unchanged; the fix is in the scraper output.elements format fully documented: Added a complete reference in the internal BAMGRID notes (docs/en/bamgrid-sitemap-extraction-notes.md, docs/sv/bamgrid-sitemap-extraction-notes.md) explaining both supported elements JSON formats:
begin/table): JSON traversal — begin navigates from root to the item array; table maps content columns to source keys (string = direct key, array = nested traversal).elements DB JSON for Disney/BAMGRID site 8 with field-mapping table and explanation of why all locales need to be considered./rss-maintenance-admin)rss_cautious_links table) so future imports of that link automatically run the dedup/purge pass after each new insert — only distinct meaningful versions are kept./rss-maintenance-admin (with non-JS form-submit fallback still intact).POST /rss-maintenance-admin/mark-cautious now handles omitted reason safely (no more Undefined array key "reason" when the field is not provided)./feed/entry/{contentId})permission:rss) now see an Admin actions panel at the bottom of every entry permalink page with:
/feed/subscriptions)channels_json=[] remains "all channels disabled" instead of defaulting back to Email (mail) in UI rendering./feed-admin)/feed page to /feed-admin. All RSS feed configuration for admins is now in one place. The public feed table column layout still reflects saved settings as before.segment anchor -> global anchor -> current date, making historical runs explicit per card./rss)elements visibility: The large elements JSON editor is now shown only for rows where sitetype is xpath, reducing table width pressure for normal RSS/WP rows.elements editor: XPath rules are now edited in an expandable/collapsible per-row panel so the main list remains compact.elements save on blur (AJAX): elements textarea changes are now saved when the field loses focus; other inline fields keep instant AJAX save on change./feed-admin category/site analysis generation now includes an Anchor date picker. Admins can generate daily/weekly/monthly/yearly analytics for earlier periods (for example last week) if a cron run was missed.anchor_date (YYYY-MM-DD) to target historical period buckets when creating/replacing cached analysis variants.app/Console/Kernel.php now schedules rss:generate-analytics --period=daily once per day at 20:00. Weekly/monthly/yearly remain available via manual CLI/API triggers./feed-admin now shows cached daily variants alongside weekly/monthly/yearly, and regenerate/replace actions refresh the page after save so overwritten category/site variants become visible immediately.docs/en/bamgrid-sitemap-extraction-notes.md and docs/sv/bamgrid-sitemap-extraction-notes.md with verified Disney sitemap structure, namespace-safe XPath candidates (url/loc/lastmod/xhtml:link), measured language-coverage impact (en-*-only would drop ~37.1% of unique IDs), and a recommended path toward a dedicated bamgrid import type with canonicalized per-content output./rss now exposes explicit xpath/json site types in Add URL flow, supports editing elements JSON rules inline when the column exists, validates elements payload format server-side, and requires elements when sitetype=xpath./feed now submits inline with JSON responses and shows status/answer directly on the page (no full reload). Non-JS fallback form behavior remains supported.rss.content data for all non-hidden feeds (categories, sources, domains, terms, entry examples). Added OpenAI profile fallback handling so missing prompt-profile config no longer hard-fails with "No prompt profile configured"./feed question context by period, allowed categories, allowed site ids, context entry cap, answer sentence cap, and mini-history size. The /feed card also shows a small recent-question history inline./feed/user-questions now supports practical pagination defaults with configurable rows-per-page to keep large/spammy histories manageable./feed/user-questions via AJAX (DELETE /feed/user-questions/{question}), with non-JS redirect fallback preserved./feed/user-questions and /feed-admin settings blocks), and these settings are applied when OpenAI answers are generated./feed/user-questions now supports filtering by status, user_id (or guest), ip, and deleted scope (active/deleted/all) to moderate noisy histories faster.POST /feed/user-questions/bulk-delete) and choose hard (permanent) or soft delete mode.feed_user_questions now stores moderation metadata for soft-deleted rows (deleted_at, deleted_by_user_id, deleted_reason) while preserving hard-delete for irreversible cleanup./feed/user-questions (POST /feed/user-questions/{question}/restore), including AJAX inline restore actions./feed-admin, the Retranslate action now reads the target language directly from the variant's stored label (e.g., a variant marked EN will always retranslate to English). The "Choose language" dropdown is removed — the labeled language IS the target./feed/subscriptions now saves channel settings inline and also handles Pause/Resume and Remove via AJAX (no full reload). Non-JS fallback submit remains supported./ dashboard and /services admin cards (route: /admin/jobs) so cron management is easier to find./feed/subscriptions now includes Discord OAuth connect flow (/oauth/discord/start -> /oauth/discord/callback), callback URL visibility, and one-click reuse of the latest Discord webhook payload, matching Slack-style onboarding behavior for webhook setup./feed now includes a mini Q&A card for questions about all open feeds. Added /feed/user-questions history page, guest Turnstile enforcement, configurable guest/user daily+weekly quotas, configurable guest concurrent intake guard, admin bypass/unlimited mode, and persistent question/answer audit records (timestamp, IP, actor metadata, status, response meta).SQLSTATE[40001] InnoDB deadlock on /api/rss/update that occurred when multiple concurrent scraper workers triggered the stale-lock cleanup (UPDATE inbound SET processlock=0 …) at the same time as the batch lock-acquisition. The stale-reset and old-row DELETE are now wrapped in a retryOnDeadlock() helper (up to 3 attempts with randomised back-off). fetchAndLockInboundBatch() is now fully atomic via DB::transaction() + lockForUpdate() (SELECT … FOR UPDATE), so two workers can never pick up the same batch./api/rss/update no longer purges old handled=0 rows by default. Purge is now opt-in (RSS_PURGE_STALE_UNHANDLED=true) and limited to abandoned locked rows (processlock=1) with deadlock-safe retries, reducing accidental backlog loss if triggers are paused.Call to undefined method App\Services\OpenAI\RequestLogService::excerpt() on POST /api/ai/socialgpt/respond by restoring safe excerpt handling in request audit logging.handled update): setEntryHandled() in RssController now executes through deadlock retry logic and explicitly releases processlock when marking rows handled, reducing SQLSTATE[40001] deadlocks during concurrent /api/rss/update workers./api/rss/feed/{selector} now support analytics-daily, analytics-weekly, analytics-monthly, analytics-yearly, and analytics-bulk (with corresponding *-analytics aliases). Feed rows now link back to related category/site targets when available. Daily period support was also added to analytics generation flows (rss:generate-analytics, POST /api/rss/analytics/run, and scheduler wiring).DB_RSS_*): config/database.php now treats DB_RSS_* as canonical while keeping legacy RSS_* fallback compatibility across host/database/username/password/charset/collation/socket/url fields, and .env/.env.example now include explicit DB_RSS_URL/DB_RSS_PORT/DB_RSS_SOCKET entries.schedule:run), manual artisan execution, and API trigger (POST /api/rss/analytics/run) in EN/SV docs.POST /api/google-home/request, /devices/query, /devices/request-sync) and a new web console at /admin/google-home. Regular users can use it when granted google-home.use permission./api/google-home/push/tokens/register, /push/tokens, /push/test) so mobile clients can register FCM tokens and receive push notifications when Google Home API calls complete.generateText() method, fixing the /feed-admin 500 error (Call to undefined method ... OpenAiEngine::generateText()).projects/nethandle-ui/README.md + CHANGELOG.md, documented script-preserving migration strategy for web-to-terminal network control, and added runtime env/config scaffold (NETHANDLE_EXEC_*, NETHANDLE_RESOLVER_BASE) in .env, .env.example, and config/services.php./feed-admin site-level generation now has a dedicated per-site title input, so site variants no longer depend on the global title field./feed analytics UI: The Feed Analytics (OpenAI cached) category block now renders only when cached analytics exist for that category, reducing empty UI noise.projects/scraper/scrape.php now fetches feed URLs with bounded curl_multi concurrency so a single slow/hanging site does not block the whole scrape batch. Concurrency/timeout can be tuned via CLI args or SCRAPE_CONCURRENCY and SCRAPE_FETCH_TIMEOUT./feed-admin, instead of only appearing after a fresh generation in the current browser session.replace_existing for the selected period/variant id, so weekly/monthly/yearly entries are overwritten intentionally instead of silently creating extra variants.meaningful_hash and tiny-change suppression (isTinyMeaninglessChange) to avoid noisy pseudo-updates (markup jitter, micro text drift) creating endless new versions./rss-maintenance-admin page lists suspicious high-churn links and supports dry-run/live noise purge per link, keeping latest representative rows per meaningful change.content.meaningful_hash with index to support semantic duplicate filtering and maintenance diagnostics.SQL/RSS_QUEUE_DIAGNOSTICS.sql with reusable admin/internal queries for pending inbound rows, stale locks, recently handled rows, payload-shape previews, and recent content insert comparisons when /api/rss/update reports errors or stuck pending items.RssController now writes structured Log::info checkpoints for inbound row start/finish and batch summaries, making it easier to correlate pending/error runs with Laravel logs (and Slack-forwarded Laravel log categories when enabled)./api/rss/update converts new rows into content, the backend now performs an immediate targeted subscription-delivery pass for affected feed ids. The existing rss:notify-subscribers scheduler remains as the fallback safety net every 15 minutes.urls.is_hidden + urls.public_hash. Hidden feeds are omitted from /feed, but can still be reached directly through /feed/key/{public-hash} and /api/rss/feed/{public-hash} without exposing numeric feed ids publicly./rss now exposes protected, useProtected (when present in schema), is_hidden, and public_hash so alert-style feeds such as Google Alerts can stay scraper-visible but public-list-hidden.projects/scraper/ondisney.php no longer depends on TorneLIB\Module\Network\Domain for URL-path parsing. The script now uses native URL parsing, reports sitemap shape samples during the run, and preserves compatibility with the existing JSON import rules by including the expected timestamp field.file_put_contents exception traces in RssController with structured Laravel Log::warning/Log::error events (including incomingidx/urlid context), so parser/import failures are centralized in Laravel logs and can be forwarded via the existing Slack log routing.projects/scraper/ondisney.php no longer assumes only d-sitemap-1..10; it now reads all available shard files from projects/scraper/sitemap-xml when present and otherwise probes remote Disney sitemap shards dynamically, restoring coverage when Disney expands to more XML files.agent_id consistently to /api/rss/data (query + payload), matching the RSS scraper identification contract used by scrape.php/trigger.php and improving backend agent attribution.projects/scraper/sitemap and projects/scraper/sitemap-mini now use dynamic shard discovery with miss-streak stop conditions instead of fixed hardcoded ranges, reducing maintenance when Disney changes shard counts.RssController after the recent cleanup pass. /api/rss/update can now convert queued inbound rows into content again instead of stalling with converted=0/errors>0 while fresh scraper payloads keep arriving.processlock=1 rows older than 30 minutes are unlocked for retry, and row-processing errors now catch all Throwables so aborted conversions stop leaving locked backlog behind.mysql, rss, mcu, gsm, spamassassin, tornis, irclog, firewall)/docsprojects/sc4a-insights and projects/socialgpt-chrome/oauth/slack/callback (a boolean false was being passed as HTTP status, causing The HTTP status code "0" is not valid.)LOG_FORWARD_DEPRECATIONS setting (default false) so noisy PHP deprecation warnings (for example PHP 8.4 dynamic-property notices) can be excluded from Slack warning forwarding/api/* requests now forward success/error events by endpoint group (for example /api/rss, /api/sms, /api/social-media-tools, etc.) so admins can enable only the API groups they care about.app_host (identifies tools.tornevall.com vs tools.tornevall.net), tokens_in, tokens_out (prompt/completion token split), response_language, feature_slug, and used_fallback_model (yes/no). Short fields are shown side-by-side in Slack for readability.context_excerpt (first 200 chars of context) and user_prompt_excerpt (first 200 chars of user prompt) are now included in the Slack audit message.Contact section in sc4a-insights/README.md with the Slack Marketplace entry for Tornevall Networks Tools (https://tornevall.slack.com/marketplace/A0AN2UJ2C4S-tornevall-networks-tools) to make Slack app/webhook setup easier to find.state may be empty on both sides, while still enforcing strict state validation whenever a state value exists. Token exchange now sends redirect_uri only when it was actually present in the authorize step, matching Slack OAuth v2 parity rules./feed now includes an admin-only Translate button next to each category analysis variant (weekly/monthly/yearly). The action is AJAX-driven and reuses the selected language from the variant language selector.sc4a-insights 2.0.0)1.0.0 2025 overlay-only release/api/social-media-tools/soundcloud/ingest/feed/entry/{contentId}), with the original source URL included as secondary fallback./feed/subscriptions now includes direct helper links for webhook setup:
Connect Slack app (get webhook) starts Slack OAuth with incoming-webhook scope and returns to subscriptionsHow to create a Discord webhook links to Discord webhook setup docsincoming_webhook.url, users can now click Use latest Slack OAuth webhook to fill the Slack webhook URL (and channel label when empty) directly in subscription settings./feed/subscriptions now shows whether Slack OAuth is configured on the current host and displays the effective callback URL, making it clearer that TOOLSAPI_SLACK is the global platform webhook while subscription webhooks come from Slack OAuth (incoming_webhook.url).incoming_webhook.url, the Slack webhook URL field is now auto-filled, saved server-side even without manual paste, and shown as readonly in the UI so users do not have to enter it manually./feed/subscriptions, so users can quickly continue sending notifications to the same Slack channel across subscriptions./donate/thankyou, intended for payment-provider completion redirects (for example PayPal return/thank-you URLs)./donate/cancel, intended as the canonical payment-provider cancellation/abort redirect./donate/farewell as a compatibility alias; it now resolves to the same aborted-donation content via the canonical cancel flow./contact, including a styled PayPal donate button and the current donation QR image.config/services.php (services.paypal.donate_url) so public donate entry points reuse the same URL.public/images/index.html with a playful “directory listing disabled” landing page, so direct browsing of the public image folder no longer lands on a blank file./feed/{id}) now show site-level analytics variants (weekly/monthly/yearly)/feed, /rss, and /feed-adminwatch_for)/feed can show different saved analysis variants via title/language selectors/feed analytics sections are collapsed by default and can be expanded per category/feed via AJAX/feed is now sorted by category orderFirst Discovery and Last Discovery are shown for feedsdocs/en and docs/svEarlier releases established the platform basics for:
Maintained by: Tornevall Networks
Last updated: 2026-04-18