Dashboard

Clients
 
Active jobs
across User · Storage · API based
Runs today
 
 
Data moved today
 

⚙️ SFTP Engine

Node 22 · Flex Consumption
Loading…

🚪 Gateway

Loading…

Recent activity (all clients)

The ten most recent runs — a glance view. These rows are not clickable; open Transfer Logs or Job History for a run's full timeline.
ClientJobCategoryStatusFilesWhen
Source of truth for clients, connections, and access — the engine and gateway read this.
CodeNameAccess typesRemote connectionStatusAction
0–0 of 0 ·
Page 1 of 1
Everyone allowed into the gateway portal. After a user signs in with Entra (MFA), the gateway confirms they're in this list and enabled, then shows only the clients they're assigned to.
UserStatusAssigned clientsLast sign-inAction
0–0 of 0 ·
Page 1 of 1
🔎 Gateway login check: Entra verifies identity + MFA → the gateway looks the user up here → if enabled, it returns their assigned clients; if missing or disabled, access is denied.
Azure workloads (Function Apps, Logic Apps, VMs with system-assigned MI) that authenticate to the gateway via Azure AD bearer tokens. The gateway validates the token's object ID (oid claim) against this list — the machine-credentials analogue to Portal users.
Display nameObject IDTenantStatusAssigned clientsLast seenAction
0–0 of 0 ·
Page 1 of 1
🔎 Gateway token check: The calling workload acquires a token from Azure AD for the gateway's audience → gateway validates signature / aud / iss / exp → looks the oid up here → if enabled, returns the assigned clients; if missing or disabled, access is denied. The client must also have the Managed Identity access type enabled (Clients → Edit) for the gateway to honour MI calls.
Transfer jobs the engine executes (and that the gateway triggers).
ClientJobSource → DestinationScheduleLast runActiveAction
0–0 of 0 ·
Page 1 of 1
Controls who can reach the gateway — machine API keys and portal users.

🔑 API keys (machine access)

This list is driven by each client's access types (set in Clients → Edit). Only clients with the api type can hold a key — the single place keys & allowed IPs are managed.
ClientKeyAllowed IPs / CIDRsCreated / RotatedAction
0–0 of 0 ·
Page 1 of 1
All transfer runs across clients (the engine writes these). Filterable; drill into the step log.
RunClientJobCategoryStatusFilesStartedDurActions
0–0 of 0 ·
Page 1 of 1
Live view — jobs the SFTP engine is currently running. Completed runs disappear here and appear in Transfer Logs.
0 active · driven by /transfer-job calls (e.g. Run Now on a job row).
JobCategoryProgressStartedElapsedAction
😴
No active transfers right now.
When a job runs via Run Now or a scheduled trigger it'll show up here with a live progress meter, then move to Transfer Logs on completion.
Health of the two managed systems and their shared resources.
Click any card for details.

⚙️ SFTP Engine

az-func-sftpengine-01
Node 22 · Flex Consumption · internal-only
Scheduler: running · queue depth 0
Last execution: 2m ago · failures (24h): 3
healthy

🚪 Gateway API

az-api-sftpgateway-01
Public · rate-limited · audited
Requests today: 96 · auth failures: 2
healthy

🗂 Storage

azsaopsglobal01 (Standard_LRS)
blob: 12 client containers · queues: sftp-transfers, notifications
tables: clients, jobs, transferLogs, clientApiAuth
healthy

🔐 Key Vault

az-kv-stargate-01
SFTP creds + API-key hashes
RBAC auth · purge protection off
healthy

✉️ Notifier & 📈 Telemetry

Notifier on az-func-stargate-mgmt-01 (Graph Mail.Send via MI) · queue-driven
App Insights az-appi-sftp-01 — receiving traces from all apps
healthy

🔑 Access Channels

External caller channels (validated at the gateway)
API keys · Managed Identities · MCP

🤖 MCP Connector

AI assistant connector (Entra OAuth broker)
az-api-sftpgateway-01/api/mcp
Live application logs from App Insights across the engine, gateway and mgmt apps — errors, warnings, info and host restarts, newest first. (App restarts show as "Host started" entries; an operator's Start/Stop/Restart is tagged 👤, an exception ⚠.)
TimeAppLevelMessage
Loading…
0–0 of 0 ·
Page 1 of 1
Browse and manage folders inside client containers on azsaopsglobal01. Work is performed by the engine, not your browser. Deletion is permanent — there is no recycle bin on this account, and every create or delete is recorded against your admin account.
Operational settings for the SFTP engine. Changes are written to engineConfig and picked up by the next run.

🧹 Maintenance purge

Daily cleanup job that scans every client container and deletes any staged blob whose name ends in _failed. These are files left behind by failed or cancelled transfers — kept for a forensic window, then purged. The typed time is interpreted in the org's system time zone; the Show in timezone picker is display-only and offers any other zone for cross-reference.

Lives in the engine alongside jobs-scheduler. Uses the engine's managed identity. Deletions audited to auditLog.

🐕 File Watchdog (stall protection)

Stops a run from hanging forever on one file. Each file's transfer must finish within the per-file timeout; if it stalls, that file is marked failed, the SFTP session is reconnected, and the run continues (finalizing as partial) instead of freezing. Keepalive detects a silently-dropped source/destination connection, and the handshake timeout bounds the initial connect. Picked up on the next run.

seconds
seconds
seconds
pings
files

A stalled read on a residential/dynamic source is the usual "stuck job" cause — these bound it so one bad file can't freeze the whole run. Recycle session every N files proactively drops and re-opens the SFTP connection mid-run before a server's per-session open limit is hit (some servers stall after a fixed number of files on one session — we saw a hard wall at ~48). Saved with the button below.

📦 Large transfers

MB

Azure cuts an HTTP request at about 230 seconds. A push that ran longer left the sender unable to tell a timeout from a failure — and a re-send delivered the file to the client twice. At or above this size the engine hands delivery to a background queue instead: the sender is acknowledged immediately, progress still shows on Job Status, and the outcome lands in History. For reference, 1.77 GB took 194s at worst and the same file varied 134s–194s between runs, so the old margin was thin. Size is only a proxy for duration — on a link a fifth as fast, 400 MB would take about 225s and still be at risk, so lower this for slow destinations. Set 0 to send everything inside the request (the previous behaviour). Applies to portal transfers; API, Managed Identity and MCP callers always deliver inline. Saved with the button below.

✓ Settings saved

🧹 Reaper (stuck-job cleanup)

The engine's self-healer for phantom jobs. A run that finishes its file work but whose worker ends before writing the final status would otherwise show as running forever in Active Jobs. The reaper finds runs with no progress for ~15 min and finalizes them (completed / partial / failed by file counts), and clears superseded queued stubs. Set how often it sweeps. The scheduler ticks every 5 min, so 5 is the minimum effective cadence. Default 15 minutes. (Saved on its own with the button below.)

minutes
✓ Saved
Dry-run a connection to verify the network path and credentials before a job tries to use them. Runs in the engine — same credentials, same network egress — and writes no transfer log.

🔌 Connection details

Enter the SFTP target directly — no client / Key Vault lookup. Useful for one-off troubleshooting against any host the engine can reach.

Test runs in the engine and uses real network calls — failures here are real failures (wrong host, port closed, bad password, fingerprint mismatch, etc.). Credentials are sent inline to the engine and not stored.

🚀 Run a test

Pick a connection above to enable the tests.

Generate a fresh SSH key pair, or validate any key (system-created or third-party, including PuTTY .ppk) to confirm it's usable and identify it. Nothing here is stored — this is a standalone utility.

✨ Generate a key pair

Creates a new Ed25519 key pair in your browser — the same generator used by SFTP connections. Download the private key to configure the transfer, and give the public key to the client's SFTP admin.

🔍 Validate a key pair

Upload both the private and public key — Validate enables only when both files are added. We first confirm they are a matching pair; if they match, we then validate each file. Encrypted private keys: enter the passphrase. An RSA PuTTY .ppk private key is auto-converted to OpenSSH first.

No file selected
No file selected

🔐 Generate PGP Encryption

Generate a file-encryption key pair (used by connections' Use file encryption → Encryption/Decryption), or validate that a public + private key are a matching pair. Supports PGP, S/MIME, and other PEM keys. Nothing here is stored — attach the keys to a connection to save them.

🔍 Validate an encryption key pair

Upload the public and private key — we detect each format (PGP / S/MIME / PEM) and confirm they look like a matching pair.

No file selected
No file selected
Authentication events across the WCM SFTP platform — Admin Portal sign-ins, Gateway API-key + Managed-Identity checks, Client Portal Entra sign-ins. Hover a row's timestamp for the full UTC value; click View for the complete event payload (geolocation, user agent, session id, suspicious-detector verdict).
WhenSurfaceIdentityUSERResultSource IPLogin TechniqueDetails
0–0 of 0 ·
Page 1 of 1
Per-job run history — one row per job-run across every client, with drill-in views for status, files, and the connection used. Complements Transfer Logs (raw step-by-step) with richer per-run detail.
Run #Client NameJob NameCategoryStatusFilesConnectionStart TimeRun Time
0–0 of 0 ·
Page 1 of 1
Conditions watched across sign-ins, transfers and configuration, the template they are announced with, and the actions they can perform. The evaluator runs every 5 minutes.

🔔 Rules

A rule watches one condition. Test dry-runs it against live data and shows what it would alert on — nothing is sent and no state is written.

RuleTypeScopeSeverityNotifiesLast firedActiveAction
Loading…
Alerts that have fired, newest first. Repeats of the same subject collapse into one row with an occurrence count; an alert that stops happening is resolved automatically. Rules themselves are authored under Triggers.

🚨 Alert Status

The Notified column carries the delivery result recorded on the alert — sent, or the reason it failed — so a notification that never arrived is visible here rather than silent. View opens everything recorded for that alert, including the evidence the rule matched on. Acknowledge accepts a firing alert and resets the rule's dedup for that subject, so the same condition raises a fresh alert instead of folding into this one — if it is still happening, expect a new alert within five minutes.

WhenSeverityRuleSubjectSeenStateNotifiedSummaryAction
Loading…
0–0 of 0 ·
Page 1 of 1
Admin-action audit trail — who changed what configuration, when, and from what → to. Covers clients, connections, jobs, users, keys, email, engine & system settings, security, and console run-control. Successful, denied, and failed attempts are all recorded.
WhenActorCategoryActionTargetClientResultDetails
Loading…
0–0 of 0 ·
Page 1 of 1
Organization-wide system behavior that applies across every page. Changes here are saved immediately and take effect everywhere the next time a list paints.

📐 Page settings

Defines the values offered in every Rows dropdown across the application. Type a number and press Enter (or click + Add value) to add it to the list. Click the radio next to any value to make it the global default — every paginated table pre-selects that value on first paint. Initial list: 5, 10, 25, 50, 100 · default 5.

✓ Saved

🌐 Time zone

Drives every Run time input and timestamp display across the application — Engine → Settings → Maintenance purge, Jobs & Schedules edit/create, and so on. The per-screen Show in timezone selectors still work, but the system time zone selected here is excluded from their lists (since it's already the default). Default: UTC.

✓ Saved

⏱ API authorization cache

How long (in seconds) the gateway may trust a validated API connection before it re-checks with this management API. Lower = a revoked key/identity stops working sooner; higher = less load + lower latency. 0 = re-check every request (instant revocation). The gateway picks up a change within a few seconds. Default 45.

seconds — max 300; this is the revocation lag
✓ Saved

🖼 Admin and User portal logo

One logo, shown in the top-left of both the admin console and the client portal, beside the word Stargate. Upload any size — it is scaled down to fit a 36 × 36 pixel box without being stretched or cropped, so a wide wordmark and a square icon both work. PNG, JPG, WEBP or GIF. SVG is not accepted, because it can carry script.

🛠 StargateAdmin Portal
Live preview of the in-app header, at the size the slider below is set to.
×
🛠 Stargate Admin Portal
Live preview of the sign-in card. The client portal shows the same thing with User Portal underneath.
×

Applies to the sign-in card of both portals — the logo and the two lines beside it. Separate from the header above because a centred sign-in card has room the sidebar does not, and each Save size button saves only its own setting.

Moving the slider restyles this page's header immediately so you can judge it in place. Save size applies it to both portals for everyone. It scales the two text lines and the logo together — they were sized to align, and moving one alone breaks that.

🧹 Clear Log History

Two separate things per area. Keep for is the automatic retention — anything older than that is purged on a daily schedule, and Never means nothing is ever purged automatically. Clear now is the manual option and it empties that area completely, however old the rows are.

Before anything is deleted — automatically or manually — the removed rows are archived to the loghistory blob container as JSON, so the data is preserved. A manual clear cannot be undone from the console. Transfer Logs and Job History are two views of one table, so they share a single retention value and one clear affects both.

Loading…

🔑 Stargate Client Credentials

The Entra app-registration credentials the MCP connector uses to sign users in (gateway OAuth broker). The gateway always reads these from here — rotate the secret without a redeploy. The secret is write-only (never displayed back).

Not set.
✓ Saved

⏱ Token Information

Lifetimes for the tokens issued to an AI assistant connector (Triggers → MCP). When an admin connects, Microsoft proves who they are once — under the tenant’s Conditional Access policy, so MFA on a compliant device — and Stargate then issues its own tokens. Those are what the assistant presents afterwards, and refreshing one does not go back to Microsoft. So these numbers, not the Microsoft session, decide how long access survives. (Saved on its own with the button below.)

minutes
hours
✓ Saved

Access token lifetime — how long one token works, and therefore the longest a disabled admin keeps access. Every renewal re-checks that they are still an enabled admin with MCP on.

Refresh window — match this to your Conditional Access sign-in frequency (currently 12 hours). Renewal never contacts Microsoft, so for as long as this window lasts the connector keeps working without re-crossing MFA or the compliant-device check. Setting it to the CA frequency means that once it lapses, reconnecting goes back through Microsoft — which puts the assistant under the same policy as everything else. A longer window is the assistant opting out of that policy for the difference.

Rotation issues a new refresh token each time and retires the old one, so a copy someone else took stops working as soon as the real connector renews. ⚠ Off by default because it is the one setting here that can break a working connector: a client that ignores the new token fails on its first renewal. Turn it on, then confirm the assistant still works after the access token expires.

🔒 Account Lockout & Sign-in History

The accounts currently locked, and how long authentication history is kept. The lockout threshold, duration and grace period are no longer set here — locking is expressed as a Trigger rule.

🔒 Account lockouts

Where repeated failed sign-ins are recorded, platform-wide — Admin Portal users (this console), Gateway API keys, Portal users, and Managed Identities. MCP connectors are tied to their owning portal user, so MCP failures count against (and lock) that user — there is no separate MCP lock. When the failure threshold is reached the account locks for the lockout duration; sign-in attempts during the lock show the time remaining. When a lock expires on its own, a grace period still applies before sign-in is allowed again (the user is told how long). An admin unlock from the Locked accounts list below skips the grace period — sign-in is active immediately.

Locking is configured under Triggers now — create a rule with the Lock the admin account action. Accounts locked by a rule appear below and can be unlocked here.

🔒 Locked accounts

Accounts locked out (or accumulating failures). This happens only while a matching Trigger rule exists and is enabled — the platform no longer does it on its own. Tick an account and click Unlock selected — an admin unlock clears the lock and the grace period, so sign-in works immediately. Locks left to expire on their own still pass through the grace period.

TypeIdentityFailuresStatus
✓ Unlocked
🔐 Login attempts retention

Industry baselines for authentication events: PCI-DSS requires 1 year minimum (90 days hot + 9 months archived); HIPAA 6 years; SOX 7 years. A pragmatic security-ops default is 90 days.

This box only trimmed this browser's copy of the page — it was never sent to the server, so it never purged the stored table. The real, server-enforced setting is Login Attempts → Keep for in Clear Log History above, which the management API purges daily. This control is left visible, and disabled, so the number you set here is not mistaken for the one in force.

days ✓ Saved

🚫 Banned IPs

Source IP addresses blocked from signing into the Admin Portal. A banned IP is refused before any credential check. IPs land here two ways: by a Trigger rule that bans on a brute-force signature (repeated failed sign-ins against accounts that do not exist) — nothing bans automatically without one — or manually, via the Ban IP button on Login Attempts or the form below. (This applies to the admin portal only — it does not affect the client portal or SFTP transfers.)

Automatic banning is configured under Triggers now — a rule such as Password spray or Brute force on one account with the Ban the source IP action.

✓ Banned
IP addressReasonBanned byWhen
Organization-wide SharePoint settings. The tenant URL anchors every client's SharePoint sources; site entries are reused across jobs so you don't retype paths.

🏢 Tenant SharePoint URL

The organization's SharePoint tenant entry point. Must end with /sites/ — do not include a specific site path here (sites are added below).

✓ Saved

📁 SharePoint sites

Named sites under the tenant. Click Browse to pick a site from the tenant, then click + Add site to commit it to the list. The browser hides sites you've already added.

No site selected

Full URL is built as /sites/.

All organization-wide email-notification config: who gets the ops/ticketing emails on failure, plus the subject + body templates the engine uses when sending. Per-user opt-ins are configured separately under Portal users → Assign clients.
About the Default template: the Default entry on each card is a reference template — it holds the factory text that Reset to default reverts to. It cannot be picked anywhere in the system as the active template for actual sending (the client wizard, Email-to-client modal, etc. all hide it). To send real emails, create at least one named template per side using + New template.

📧 Failure notifications

Failure notification is configured under Triggers now — a Transfer failed or partial rule with a Send an email action, which can target any recipients and template.

📗 Successful transfer template

Sent to every recipient who opted into success notifications for the client (per-user opt-ins under Portal users → Assign clients). You can save multiple templates here and pick which one is active for the engine to use.

Variables (click to insert):
{{client}} {{client_code}} {{client_fname}} {{client_lname}} {{portal_user_fname}} {{portal_user_lname}} {{host}} {{job_name}} {{run_id}} {{date}} {{file_count}} {{file_names}} {{file_results_table}}

📕 Failed transfer template

Sent to every recipient who opted into failure notifications for the client, plus any recipients a Trigger rule adds (the per-client opt-ins are the only list now — the page-level "ops firehose" recipient list was removed). You can save multiple templates here too.

Variables (click to insert):
{{client}} {{client_code}} {{client_fname}} {{client_lname}} {{portal_user_fname}} {{portal_user_lname}} {{host}} {{job_name}} {{run_id}} {{date}} {{file_count}} {{file_names}} {{file_results_table}}

📎 Attachment Size Limit

Maximum total size of file attachments the engine will include in one Email connection message. If a transfer's staged files exceed this, the send is rejected and the run fails with a clear size-limit error. Range 1–150 MB.

MB total

📧 Email Template

Used by Email connections (the engine sends the transferred files as attachments using the selected template) and by the Email to client button on the Clients page (to deliver a generated public key). Each template carries a From address (the sender), subject, and body, with the same variables as the Success / Failure templates so you can address the client by name and include run context. Save multiple variants and pick one per Email connection.

Variables (click to insert):
{{client}} {{client_code}} {{client_fname}} {{client_lname}} {{portal_user_fname}} {{portal_user_lname}} {{host}} {{job_name}} {{run_id}} {{date}} {{file_count}} {{file_names}} {{file_results_table}}

📬 Mailbox Recipient

Mailboxes the engine works with. Search the directory and pick one to add it, then set two options per recipient: Send As — the engine may send email templates from this mailbox (adds it to the send-as group); and Receive As — the mailbox becomes selectable in a ReceiveEmail connection's Mailbox Access list for the engine to read. A recipient can have either or both.

✓ Saved
Configure a public-holiday API so scheduled jobs can opt in to skip runs that fall on a holiday. The engine fetches the holiday list once per year (cached) and each scheduled job can independently toggle Skip on holidays.

📡 API endpoint

URL the engine calls to retrieve the year's holiday list. The response must be a JSON array of objects each with at least a date (ISO-8601) and a name field. Free public option: https://date.nager.at/api/v3/PublicHolidays/{year}/{country}.

✓ Holiday API saved
Backups of the platform's configuration, its Key Vault secrets and the history behind every report, stored in the private recovery container. A restore adds and overwrites what a backup contains and never deletes anything newer.

🔑 Backup encryption key

Every backup is sealed with this key, and a snapshot can only be restored or browsed while the key that sealed it is loaded. Use Generate a new key below and the file downloads to you, or upload an RSA private key you already hold, in PEM form and without a passphrase. Keep your copy of that file somewhere safe: it is the only copy outside Key Vault, and it is what makes a lost Key Vault secret recoverable rather than fatal.

🕒 Scheduled Backups

The evaluator checks every 15 minutes and takes a backup when one is due, so any interval or daily time can be set here. Retention here removes the oldest scheduled backups beyond the count you keep; manual backups follow their own limit in Manual Backup Retention below.

🧮 Manual Backup Retention

How many manual backups to keep. Once a new manual backup pushes the count past this number, the oldest are deleted first until only that many remain. Leave it at 0 to keep every manual backup, which is how the platform behaves today.

💾 Backups

Every backup — manual or scheduled — captures configuration, report history and all Key Vault secrets, sealed with the encryption key above. A snapshot can only be restored or browsed while the key that sealed it is loaded; it can always be downloaded or deleted.

Pick a .json.gz file downloaded from this list to put it back. Its key fingerprint is read from the file itself, so a snapshot sealed with a different key imports with Restore and Browse unavailable until that key is loaded.

TakenByKindContentsSizeAction
Loading…

♻ Restore

Every restore that has been performed, newest first. Applied is how many records were added or overwritten — a restore never deletes, so anything created after the backup survived it. Each restore first takes a safety backup of the configuration as it was immediately beforehand; that is what to restore from if a restore itself was the mistake.

WhenByRestored fromSectionsAppliedFailedSafety backupAction
Loading…
Accounts that can sign into the Admin Portal. Add either a Microsoft Entra user (signs in via SSO) or a Local account (username + password stored here). The hidden sgadmin super-admin account exists outside this list and cannot be edited or removed.

👥 Portal admins

Click + Add Entra user or + Add local user to grant access. Entra users authenticate via Microsoft single sign-on; local users authenticate against the stored hash here.

Display nameUsername / emailTypeStatusLast sign-inAction
Admin console guide
🏠 Help home
🚀 Getting Started4
🏢 Clients4
📄 Tools2
⏱ Jobs & Schedules5
📜 Logs & History4
🗂 Storage1
👤 Portal Users3
👥 Admin Users2
🔑 Machine Access2
⚙️ Global Settings5
🔒 Security4
📈 Monitoring4
🛟 Troubleshooting1
📄 Triggers5
📄 Backup & Restore3
🤖 AI Assistant (MCP)1

❓ Stargate Admin Console — guide

How every section of this console works: what each page does, what the fields mean, and which actions cannot be undone. 49 topics.

Pick an area below, or use the search box on the left. Each topic has Previous / Next links so you can read a section straight through.

💡 Connecting an AI assistant to this console? See 🤖 Connect an AI assistant (MCP) — the full setup guide is downloadable there.

🤖 Connect an AI assistant (MCP)

AI Assistant (MCP)

You can drive this console from an AI assistant instead of the browser. The assistant signs in as you through Microsoft — there is no key or password to share — and gets the console's own capabilities, with every change recorded in System Changes under your name.

Access is granted per account and is off by default. An administrator enables it under Global Settings → Users → 🤖 MCP, where the tool policy is also chosen. Only Entra (SSO) admins can use it: local accounts and sgadmin never can.

📄
stargate-admin-mcp-setup.md
The complete setup guide — endpoint, the four prerequisites, connector commands for Claude Code and claude.ai, first checks, destructive-operation warnings and a troubleshooting table.

The endpoint, if you just need that: https://az-func-stargate-mgmt-01.azurewebsites.net/api/mcp. A compliant client discovers everything else from the URL.

⚠ A connected session has full production admin access. Deleting a client, banning an IP, deleting folders and clearing logs are all irreversible — see Editing and deleting a client and Banned IPs.

The same guide is available to a connected assistant through its get_help tool, so you can simply ask it to read the setup guide.

🚀 Signing in to the console

Getting Started

The console opens on a sign-in overlay titled Stargate Admin Portal with two choices.

1 Entra SSO — then checked against the Users list· 2 Local Login reveals username & password — covers local accounts & sgadmin
  • Sign in with Microsoft Entra — a Microsoft pop-up appears; pick your account and complete MFA. Your address is then checked against the Admin Portal account list (Global Settings → Users). If it is not there you are told the account is not on the Admin Portal list; if the account is disabled you are refused. Being able to authenticate with Entra is not enough — you must also be listed and enabled.
  • Local Login — username and password. This covers local accounts created under Users, plus the hidden sgadmin super-admin break-glass account. sgadmin sits outside the Users table, cannot be edited or removed, and its password is managed in configuration rather than in the console. Only sgadmin can add new local accounts.
💡 If Entra sign-in is refused, check the Users list before troubleshooting Entra — authenticating successfully says nothing about whether the account is listed and enabled here.

Failed local attempts are counted and appear on Login Attempts, but they no longer lock the account on their own — there is no automatic lockout policy, and no attempts-remaining countdown. An account is locked only when a Trigger rule with the Lock the account under attack action fires, or an administrator locks it; a locked account then shows a locked message, and an administrator can unlock it immediately.

If your source address is on the Banned IPs list, sign-in is refused with a network-blocked message no matter what credentials you use; an administrator must unban the address. Every attempt — success, failure, or denial — is recorded on Login Attempts.

There is no "forgot password" on this page. The link that used to sit there did nothing, so it was removed. A local admin's password is reset by another admin under Users; the sgadmin break-glass password is held in Key Vault, which is where to look it up.

🚀 Console layout

Getting Started

The left sidebar is headed 🛠 Stargate Admin Portal, with 📊 Dashboard on its own at the top and everything else in four groups.

🛠 Stargate Admin Portal
📊 Dashboard
Configuration 1
👥 Clients
🔁 Jobs & Schedules
⚡ Triggers
Engine
🛰 Active Jobs
📜 Transfer Logs
🔌 Connection Test
Logging & History
🚨 Alert Status
📋 Job History
Global Settings
🛠 System Settings
👥 Users
Dashboard ❓ Help2 prod BJ3
Data moved today
12 transfers · 340 files delivered
1 Four nav groups — Configuration, Engine, Logging & History, Global Settings· 2 ❓ Help opens this guide· 3 Your account menu — password, card layout, sign out
  • Configuration — the records the platform runs on: Clients, Portal users (who may enter the client portal and which clients they see), Jobs & Schedules, API Keys (machine access to the gateway), Managed Identities (Azure workloads that authenticate by token), and Triggers, where you author the conditions that raise alerts, the template they are announced with, and the actions they perform.
  • Engine — day-to-day operations: Active Jobs (live runs), Transfer Logs (every run, with step detail), System & Health, Settings (engine tunables), Connection Test, Key Pair, and Folder Management for browsing client blob containers.
  • Logging & HistoryAlert Status (alerts that have fired, each with the delivery result of its notification), Login Attempts, Job History, and System Changes, the admin audit trail of who changed what.
  • Global Settings — organisation-wide configuration: System Settings, SharePoint, Email Notifications, Holiday API, Users (who can sign into this console), and Backup & Restore, which captures the configuration, the Key Vault secrets and the log history behind every report.

The top bar shows the current page title on the left. On the right sit the ❓ Help button, a prod environment pill, your initials, and your name. Clicking that block opens a small menu with your display name and email, 🔑 Change password — shown only for local accounts, since Entra passwords are managed in Entra — ↺ Reset card layout (see arranging cards), and ⎋ Sign out.

Destructive actions require typing "Delete"

Anything that permanently destroys data — deleting a client, job, user, connection or template, revoking

a key, removing files in Folder Management, and the like — opens a confirmation box where you must **type

the word Delete** before it proceeds. One box covers the whole action, even when it removes many items

at once; a wrong or empty word cancels safely. Two flows keep their own, stronger gates: deleting a folder

asks for the folder's own name, and clearing the backup encryption key asks you to type Clear.

The same rule applies to machine callers: destructive admin MCP tools require the argument

confirm: "Delete".

On a phone or tablet

The console adapts to the screen it is opened on. On a tablet the sidebar simply narrows and everything else stays put. On a phone the sidebar tucks away: tap the button in the top bar to open the menu, and tap a menu item or the dimmed area beside it to close it. Wide tables scroll sideways within their own box rather than squeezing their columns. Every view works on a phone, though the dense editors — the job and connection forms — are more comfortable on a bigger screen.

🚀 Arranging the cards on a page

Getting Started

Most settings pages are built from cards, and you control both their order and whether they are open. Both are saved against your own account: rearranging a page as one admin changes nothing for anyone else, and your arrangement follows you to any browser or machine you sign in from.

▸ ⠿ Email Notifications1
▾ ⠿ SharePoint2
Tenant URL · site · library
▸ ⠿ Holiday API
↺ Reset card layout puts every page back to its built-in order
1 Drag the ⠿ grip to reorder a card· 2 ▸/▾ caret expands each card independently

Cards start collapsed, so a page opens as a clean list of headings. Click a heading, or the caret beside it, to expand that card; click it again to collapse it. Cards open and close independently — opening one does not close the others — and whatever you leave open is what you come back to.

To reorder a page, drag the grip to the left of a card's heading and drop it where you want it. The grip is the only thing that drags, so the buttons and inputs inside a card keep working normally.

Two things behave differently, by design:

  • System & Health — the tiles collapse like any other card, but only the caret or the heading toggles them. Clicking the body of a tile still opens its drill-down, as it always did.
  • A card that appears because you just asked for it — Connection Test results, the Holiday API holiday list — opens rather than showing you a shut bar.

The Dashboard is excluded, as is any page holding a single card, since there is nothing there to arrange.

↺ Reset card layout in the profile menu puts every page back to its built-in order with all cards collapsed. It affects only your account, and it cannot be undone.

🚀 Dashboard tiles

Getting Started

The Dashboard opens on four figures across the top.

Clients
81
6 active · 2 disabled
Active jobs
5
7 defined
Runs today
92
7 ✓
2 ✗
Data moved today
2.4 GB
86 files3
1 Four figures across the top· 2 Green ✓ completed · red ✗ failed or cancelled· 3 Data moved today — bytes, with files delivered beneath
  • Clients — how many client records exist, with a breakdown underneath of how many are active versus disabled.
  • Active jobs — jobs currently enabled. The line beneath gives the total number of jobs defined.
  • Runs today — transfer runs recorded today, with a green count of completed runs and a red count of failed or cancelled ones.
  • Data moved today — total bytes transferred today, and beneath it the number of files that succeeded.

A tile showing means the figure has not loaded yet. That is deliberate: a dash is used rather than a zero so a failed load can never look like a real result of nothing. If a dash persists, the underlying fetch failed.

💡 Read a persistent dash as a broken fetch, not as a quiet day — nothing on the Dashboard is a substitute for Transfer Logs when you need to know what actually ran.

Below the tiles are two health cards, ⚙️ SFTP Engine and 🚪 Gateway, each with a status pill fed by the platform health check. They start neutral and only turn into a health verdict once real data arrives. System & Health has the detail behind those pills.

Recent activity (all clients) lists the latest runs across every client with columns Client, Job, Category, Status, Files, and When. Click any row to open that run's full transfer timeline (the same detail described under Job History). The pager underneath sets rows per page and moves between pages.

🏢 Clients and the New Client wizard

Clients

A client record represents one external counterparty you exchange files with. It is the source of truth that both the engine and the gateway read, and it holds the code, name, contact details, access types, notification templates, and connections.

🔍 Search code / name / hostAccess type: All ▾Status: All ▾Sort: Default ▾Clear+ New client1
CodeNameAccess typesRemote connectionStatusAction
ACMEAcme Corp👤 ⏱ 🤖sftp.example.com +22ActiveEdit Delete
GLOBEXGlobex Ltd🛡team.example.com / ReportsDisabledEdit Delete
1 + New client opens the five-step wizard· 2 Remote connection summarises the first connection, +N for more

The clients table

The table has columns Code, Name, Access types, Remote connection, Status, and Action. Remote connection summarises the client's first connection — an SFTP host, or a SharePoint site and library, or an email recipient or mailbox — with a +N badge when there are more. The filter bar searches code, name, and host, and narrows by access type and status. Each row offers Edit and Delete, both covered under Editing and deleting a client.

The New Client wizard

+ New client opens a five-step wizard:

  • Client info — Name, Code (forced to upper case), contact first name, last name, email, and Status.
  • Connections — add one or more connections of type SFTP, SharePoint, SendEmail, or ReceiveEmail. At least one is required.
  • Access — the access types, plus an API key and allowed-IP panel when API is selected.
  • Notifications — the success and failure email templates this client uses.
  • Review — the exact record about to be written.

On Create client the client is written first, then each connection is saved with its credentials in Key Vault, and a per-client blob container is provisioned. Jobs are added separately under Jobs & Schedules.

⚠️ The code cannot be changed afterwards — get it right before you click Create client.

🏢 Access types

Clients

Five access types gate how a client can be reached: 👤 Portal, ⏱ Scheduled, 🤖 API, 🧩 MCP, and 🛡 Managed Identity. You set them for the client under Edit → 3 · Access type, and again on every individual connection under Access type selection. The connection-level choice controls which planes may see and use that particular connection and is independent of the client's own list. A connection with nothing selected cannot be saved.

Access typesAccess type: All ▾1
👤 PortalAssigned portal users move files ⏱ ScheduledEngine may run scheduled jobs 🤖 APIMachine callers via API key + allowed IPs 🧩 MCPAI assistants via the /mcp endpoint 🛡 Managed IdentityAzure workloads by MI token
Set on the client (Edit → 3 · Access type) and again on every connection2
1 Access type filter narrows the list — All · Portal · Scheduled · API · MCP · MI· 2 The connection-level choice is independent of the client's
Access typeWhat it allows
portalAssigned portal users can see this client in the client portal and move files for it.
scheduledThe engine may run scheduled jobs for this client. Only clients with this type appear when you pick a client in the Jobs wizard.
apiMachine callers may reach the client through the gateway's REST surface using an API key plus an allowed-IP list. Only clients with this type can hold a key, and keys and IPs are managed on the API Keys page.
mcpThe client can be reached through the gateway's /mcp endpoint by AI assistants, with the same permissions and full auditing. Tool policy is set per portal user; local accounts, including sgadmin, can never use MCP.
miAzure workloads may call on behalf of this client using a managed-identity token. The calling identity's object ID must also be registered and enabled under Managed Identities, otherwise the call is denied.
💡 Because the two levels are independent, granting a type on the client is not enough — the connection you expect to be used must select it too.

🏢 Client connections

Clients

Connections are the endpoints a client transfers against. They are all equal, independently editable named rows — there is no longer a primary or default connection, so every connection needs a Connection name, and a job picks whichever one it needs. Add them in wizard step 2 or under Edit → 2 · Connections, choosing a type and clicking + Add connection.

2 · Connections
Connection name *Nightly_Report1
ProtocolSFTP ▾
Hostnamesftp.example.com
Port22
Username (Key Vault)svc_acme
Auth methodPrivate key (in Key Vault) ▾
Remote path/inbound 📁 Browse🔍 Fetch Thumbprint2
+ Add connection
1 Every connection needs a name — no default connection· 2 Remote path is the working folder; Fetch Thumbprint pins the host key
TypeWhat you configure
SFTPProtocol (SFTP, FTPS, or FTP), Hostname, Port, Username, and an Auth method of either Password or Private key with optional passphrase. Credentials are held in Key Vault; the connection row stores only the pointer. 🔍 Fetch Thumbprint has the engine collect the server's SSH host-key fingerprint and pin it, so a changed host key is refused later. Remote path is the starting folder on the server, and 📁 Browse walks the live folder tree so you can pick it. This path is the connection's working directory, and it is the folder every job using this connection works in — jobs no longer carry a remote directory of their own, so this is the only place it is set.
SharePointThe tenant URL is read-only from Global Settings; choose a SharePoint site and Document library, then Folder, browsed live from Graph. Leave the folder blank to use the library root.
SendEmailTo, CC, and an Email Template that supplies the sender address.
ReceiveEmailA Receive-As Mailbox Access, plus From Address and Subject Line filters.

Each type can also enable file encryption.

💡 Prove a host, port and credential on Connection Test before you commit them into a connection.

🏢 Editing and deleting a client

Clients

Editing

Edit opens the client in four collapsible sections: 1 · Client info (name, contact, status — the code is locked), 2 · Connections, 3 · Access type, and 4 · Notifications. Save changes writes the client details, then reconciles the connections shown: existing ones are updated, new ones added, and any card you removed is deleted from the backend. Flipping Status between Active and Disabled also cascades to that client's jobs, disabling or re-enabling them with the client.

Edit client — Acme Corp (ACME)
1 · Client info2 · Connections3 · Access type4 · Notifications
NameAcme Corp
Code (locked)ACME 🔒
Contacts1
First nameJane
Last nameDoe
Email *ops@example.com
+ New contact
StatusActive ▾
3 · Access type
👤 Portal⏱ Scheduled🤖 API🧩 MCP🛡 Managed Identity
4 · Notifications
📗 Successful transfer templateGlobal Success Notification ▾
📕 Failed transfer templateGlobal Failure Notification ▾
CancelSave changes2
1 Contacts — First/Last name + Email; + New contact adds more· 2 Save changes writes details, reconciles connections; Delete lives on the client's row

Deleting

Delete is on the client's row; the final confirmation requires typing the word Delete — a

client deletion is irreversible and purges its secrets, connections and container.

⚠️ Deleting a client is irreversible — there is no undo and no recycle bin.

Deleting a client removes:

  • the client record itself, its jobs, and any API key;
  • every Key Vault credential secret belonging to it, including the per-connection secrets;
  • all of its named connection rows;
  • the generated key-pair blob held for that client;
  • the client's blob data container, along with the data and archive contents inside it;
  • the client's code from every portal user's assigned-clients list.

Copy anything you still need out of Folder Management first. The cleanup steps are best-effort and never block the delete, so if you intend to reuse the same code, confirm afterwards that the container and secrets really are gone.

📄 Connection Test

Tools

Connection Test, under Engine, dry-runs a connection so you can prove the network path and credentials before anything depends on them. You type the target in directly — there is no client lookup — so it also works for one-off troubleshooting against any host the engine can reach.

🔌 Connection details
Host / IPsftp.example.com or 192.168.1.10
Port22
Usernamesvc_acme
Auth methodPassword ▾
Password••••••••
Remote path (optional)/upload
Expected host fingerprint (optional)SHA256:…
🚀 Run a test
📡 Basic connectivity test1🔐 Authentication test2
Enter at least a host to run the basic connectivity test — credentials are sent inline and not stored, and no run appears in Transfer Logs.
1 Basic test — DNS · TCP · SSH banner · fingerprint; no credentials· 2 Auth test pulls creds from Key Vault, logs in and lists the remote path

Fill in Host / IP and Port, then optionally Username and an Auth method of Password or a private key file (OpenSSH PEM or PuTTY .ppk, with a passphrase field if the key has one — Key Pair can validate one for you). Remote path and Expected host fingerprint are optional.

Two tests are offered:

  • 📡 Basic connectivity test — DNS resolution, TCP connect, SSH banner, and the host fingerprint. No credentials are used, so a host alone is enough to run it.
  • 🔐 Authentication test — everything above, plus a real login and a listing of the remote path. This needs a username and either a password or a key.

Results appear as a step timeline with a status and duration per step, a pass or fail badge, total elapsed time, and the live host fingerprint. The test executes in the engine using the platform's real network egress, so a failure here is a genuine failure — wrong host, closed port, bad password, fingerprint mismatch. Credentials are sent inline and are not stored, and no run appears in Transfer Logs. Use it to confirm a host and credential before committing them into a connection.

💡 Because the test runs from the engine's own egress, rule out host, port, credential and fingerprint here before you go looking for a fault in a job.

📄 Key Pair

Tools

Key Pair, under Engine, is a standalone utility with four cards. Nothing on this page is stored — everything is download-only, and keys only persist once you attach them to a connection.

✨ Generate a key pair
Fresh Ed25519 SSH pair, generated in your browser via Web Crypto
✨ Generate new Ed25519 pair
Public keyssh-ed25519 AAAAC3Nz…8fJk id_ed255191
⬇ id_ed25519⬇ id_ed25519.pub🗑️ Clear keys2
⚠ Nothing here is stored — download both files before you leave
🔍 Validate a key pair
Choose the private & public files — checks they match, then validates each
🔐 Generate PGP Encryption
OpenPGP pair for connections using file encryption
🔍 Validate an encryption key pair
Detects PGP / S/MIME / PEM and confirms they match
1 Hand the public key to the client's SFTP admin· 2 Download-only — Clear keys wipes them for good
⚠️ Download both files before you navigate away or click 🗑️ Clear keys. Nothing generated here is kept, so a private key you did not save is gone.
  • ✨ Generate a key pair creates a fresh Ed25519 SSH pair in your browser using Web Crypto — the same generator SFTP connections use. Download the private key as id_ed25519 and the public key as id_ed25519.pub, then hand the public key to the client's SFTP administrator. 🗑️ Clear keys wipes them from the page.
  • 🔍 Validate a key pair confirms a key is usable and identifies it. Choose both the private and public files — Validate stays disabled until both are present. It first checks that they are a matching pair, then validates each file. Enter the passphrase if the private key is encrypted; an RSA PuTTY .ppk is converted to OpenSSH automatically. Once the key is attached, Connection Test proves it against the live host.
  • 🔐 Generate PGP Encryption produces a real OpenPGP pair with no passphrase, for connections using file encryption: attach the public key to an Encryption slot and the private key to a Decryption slot — a job's file rows can then enable PGP encryption. For S/MIME, upload your own X.509 certificate and PEM key instead.
  • 🔍 Validate an encryption key pair takes a public and private file, detects each format (PGP, S/MIME, or PEM), and confirms they match.

⏱ What a job is, and reading the jobs table

Jobs & Schedules

A job is a transfer the engine executes for one client: it moves files from a source connection to a destination connection on a schedule you define. Jobs live under Jobs & Schedules; the engine runs them, and the gateway can also trigger them.

🔍 Search client / job / connectionClient: All ▾Sort: Default ▾Clear
ClientJobSource → Destination1ScheduleLast runActiveAction
ACMENightly_ReportSchedule Based conn-a → conn-bEvery 40mcompleted 32/322
8/18/2026, 3:57 PM
3▶ Run Now Edit Delete
ACMEHourly_FeedSchedule Based conn-a → conn-cCron: 0 2 * * * 📅 skip-holfailed 0/2
8/18/2026, 2:00 PM
🔒 off▶ Run Now Edit Delete
← PrevNext →Rows 5 ▾
1 Source → Destination shows badge & connection names·2 Last run: outcome, file count, timestamp·3 Active toggle enables or pauses the job

Reading the table

The table has seven columns:

ColumnWhat it shows
ClientThe client the job belongs to.
JobThe job name.
Source → DestinationA category badge and the real connection names.
ScheduleCron: 0 2 *, Every 15m, or On request, plus a 📅 skip-hol badge when the job skips holidays.
Last runThe last outcome, a succeeded/total file count and a timestamp — that is the genuine last run, scheduled or manual.
ActiveThe toggle that enables or pauses the job.
Action▶ Run Now and Delete.

Filter with the search box (client, job or connection), the Client dropdown and Sort; Clear resets, and the Rows selector sets page size.

Active, locked and deleted jobs

The Active column toggle enables or pauses a job. It saves immediately and is recorded in System Changes. If a job's client no longer has the Scheduled access type, the job is locked: the toggle is disabled, a 🔒 schedule off badge appears, and Run Now is greyed out until you re-enable Scheduled on the client. Delete removes the job outright.

⏱ Creating a job

Jobs & Schedules

Click + New job. Required fields are marked with a red asterisk.

New job
Client *Acme Corp ▾1
Job name *Daily Upload
Source Connectionconn-a ▾
Destination Connection *conn-b ▾
📄 Files to transfer2 No files defined
FrequencyDaily ▾
StateActive ▾
CancelCreate job3
1 Pick a client with Scheduled access·2 Define the file patterns to move·3 Create, then set the schedule
  • Client — pick the client. If it lacks the Scheduled access type you get a red warning and cannot create the job; fix that under Clients first.
  • Job name — free text, for example Daily Upload.
  • Source Connection and Destination Connection — both are required and they must be different connections. These are the client's connections defined under Clients → Edit.
  • State — Active or Paused.

Files to transfer

Click 📄 Files to transfer to define what moves. Add one row per file with + Add file. Date tokens go in parentheses, for example data_count_(MM-dd-yyyy).csv. Whatever you put between the parentheses is the format — yyyy, yy, MM, MMM (Aug), MMMM (August), dd, HH, mm, ss, in any arrangement, with - _ . / and spaces kept as written — so (yyyyMMdd), (MMddyyyy), (MM-dd-yyyy), (yyyy_MM_dd) and (dd-MMM-yyyy) all work. Wildcards work too: report_*.csv matches anything fitting the pattern.

A date token selects that one date's file. ABC_(yyyyMMdd).csv on 12 August picks up ABC_20260812.csv and leaves ABC_20260811.csv alone, so stale files in the source directory are not swept along. Combine a token with a wildcard when you do want several files from the same day: report_*_(yyyyMMdd).csv takes every one of that day's reports. A Date Offset shifts which date is chosen — subtract 3 on a Monday and the job looks for Friday's file. When nothing matches, the run names the exact filename it looked for, which is usually enough to spot a format mismatch.

Delivery overwrites a file of the same name at the destination, deliberately: re-sending is how a corrected file replaces an earlier one. If the very **first row is just *, the engine lists the source directory and transfers every file it finds, and later rows act only as overrides for date formatting, offsets and encryption. Save with Save files**; the summary next to the button shows the pattern count.

Per-file options

Each row can enable a Date Formatter, a Date Offset, and two encryption options of its own:

  • 🔓 Decrypt the source file — decrypt the arriving file with the source connection's Decryption private key. Files named .pgp, .gpg or .asc auto-decrypt regardless; ticking this forces decryption for files that don't say so in their name.
  • 🔒 Encrypt the destination file — encrypt with the destination connection's PGP Encryption public key before sending; the client receives .pgp.

Each option is offered only when the matching key actually exists — decryption needs a Decryption private key on the source connection, encryption a PGP Encryption public key on an SFTP destination. Without the key the checkbox is disabled and its tooltip says why. The encryption options are independent of the Date Formatter: a row can decrypt or encrypt without any date token.

⚠️ A first row of * transfers everything in the source directory, not just the files you had in mind. Use an explicit pattern unless you really do want the whole folder moved on every run.

Finish with Create job, then set the frequency and retry window. A job with no files defined will refuse to run.

⏱ Schedule options, time zone, retries and holidays

Jobs & Schedules

Frequency and run time

Frequency offers seven options. Interval choices run around the clock; the rest start at the Run time you set.

FrequencyDaily ▾1
Run time (system tz)02:00
Show in timezoneAmerica/Chicago ▾ viewer only
⚙️ Advanced Options
Run for (days after schedule)12
Retries per hour4
✅ Mark successful only when file(s) complete
📅 Skip on holidays🗓 Customize…3
1 Frequency sets when the job fires·2 Retry window keeps chasing missing files·3 Customize holiday exceptions per job
FrequencyWhat it does
Every N MinutesN from 1 to 1440, around the clock.
HourlyEvery N hours, 1 to 24, around the clock.
DailyOnce a day at the Run time.
WeeklyPick a day; runs at the Run time.
WeekDayTick any of Mon–Fri; runs at the Run time.
MonthlyMonth plus day of month; runs at the Run time.
BusinessDay1 = the first weekday of the month, weekends skipped.

Run time is interpreted in the organisation's system time zone, set under Global Settings → System Settings — the label next to the field tells you which. The Show in timezone dropdown only converts that moment for your reference; it never changes when the job runs.

⚠️ Show in timezone is a viewer, not a setting. Change it and the job still fires at the Run time in the system time zone.

Advanced options

Under ⚙️ Advanced Options:

  • Run for (days after schedule) — the forward retry window. When a scheduled run finishes without delivering everything, the engine keeps re-attempting the files that are still missing until the job completes or this many days pass. 0 means a single attempt, no retries; maximum 30.
  • Retries per hour — cadence inside that window, 1 to 12 (4 = roughly every 15 minutes, 12 = roughly every 5).
  • Mark successful only when file(s) complete — the run counts as successful only once the expected files transfer; if the window is exhausted without completing, the job is marked failed.

Retries re-attempt only the files that have not been delivered yet — anything already delivered is never re-sent, and files delivered by a manual Run Now during the window count too. Each attempt is recorded in Transfer Logs and Job History as its own run with the Schedule Retry category, so a chased file leaves a visible trail. The window closes early when the job completes, when an operator cancels a run, or when the schedule's next regular fire arrives and takes over.

(Per-file decrypt and encrypt options live on each file row under Files to transfer, not here.)

Holidays

📅 Skip on holidays needs the Holiday API configured, otherwise it is disabled. When on, every holiday is skipped. 🗓 Customize… opens Holiday exceptions: uncheck any holiday this job should still run on. Exceptions are per-job and change nothing globally.

⏱ Running a job now

Jobs & Schedules

▶ Run Now on a job row queues an immediate run, whatever the schedule says. It does not consult the frequency or the holiday settings — it runs once, now.

ClientJobScheduleLast runAction
ACMENightly_ReportEvery 40mcompleted 32/32
▶ Run Now1EditDelete
ACMEPaused_Feed 🔒 offDaily 02:00
▶ Run NowEditDelete
Once accepted it appears as Manual Based in Transfer Logs & Job History2
1 Run Now queues an immediate run·2 Logged as Manual Based, distinct from the schedule
⚠️ There is no dry run. Run Now moves real files as soon as the engine accepts it, holiday exceptions included.

Two things stop it. A paused job is refused with a prompt to activate it first, and a job whose client has lost Scheduled access is refused as locked. A job with no file patterns will also fail, so define Files to transfer first.

While the request is being placed the button reads Queuing…. Once the engine accepts the run and returns a run id, the console adds a live row and moves you straight to Active Jobs, then polls the engine for progress. If the live view is unavailable you get a confirmation showing the run id instead, and you can watch progress under Active Jobs or Transfer Logs.

Expect the run to appear as queued first — accepted, but the background worker has not picked it up yet — then progress through per-file transfer to a terminal status. When it finishes, the live row clears, the job's Last run cell updates with the outcome and file counts, and the full record appears in Transfer Logs and Job History labelled Manual Based, so hand-triggered runs stay distinguishable from the schedule's own.

If the job has an open retry window, whatever the manual run delivers counts toward it — the window keeps chasing only the files still missing.

Every manual run is written to System Changes against your admin account, with the run id.

⏱ Active Jobs — watching and cancelling a run

Jobs & Schedules

Active Jobs is a live view of transfers the engine is running right now. It is not limited to runs you started: the console polls the engine's active-run registry every few seconds and adopts in-flight runs from every channel, so scheduled, portal, API, Managed Identity and MCP transfers all appear. Completed runs disappear from here and turn up in Transfer Logs.

1 active · live view↻ Refresh
JobCategoryProgressStartedElapsedAction
Nightly_Report Manual Based running 2/3 · 64%1
report_%DATE%.csv — transferring 42% (3.1 MB / 7.4 MB)
14:02:10 00:41 Cancel2
1 Live progress bar with files done / total·2 Cancel stops the run & closes its retry window

Columns are Job, Category, Progress, Started, Elapsed and Action. The Category badge shows where the run came from — Scheduled Based, Schedule Retry, Manual Based, User Based, API Based, MI Based or MCP Based. Progress gives an overall bar with the status, files done over total, and a percentage. Beneath it, a per-file line names the file currently moving and reads sending…, transferring 42% (3.1 MB / 7.4 MB) or ✓ sent. A running tally shows done and failed counts, and Show file outcomes expands the per-file results. Elapsed ticks every second.

Cancel signals the engine to stop the run. The engine flags it, finalises the log row as cancelled and marks every un-sent file failed. Cancelling also closes the job's open retry window, so the engine will not keep re-attempting a run an operator stopped.

⚠️ Cancelling does not undo delivered files. A file already in flight finishes server-side, and anything already delivered stays on the remote.

Finished rows offer Dismiss instead, which only clears the row from this view. Both actions are audited in System Changes.

📜 Transfer Logs — filtering and columns

Logs & History

Transfer Logs lists every transfer run across all clients, newest first, from the records the engine writes. It loads the most recent 200 runs; ↻ Refresh reloads. Runs still in flight live on Active Jobs instead.

🔍 Search run / client / jobStatus: All ▾1Category: All ▾When: All ▾Sort: Newest ▾Clear↻ Refresh
RunClientJobCategoryStatusFilesStartedDurActions
a1b2c3d4ACMENightly_ReportScheduled Basedcompleted32/322026-08-18 15:25:1132m 4sView2
e5f6a7b8ACMEHourly_FeedManual Basedpartial3/32 ✗292026-08-18 12:53:030.0sView
‹ PrevNext ›Rows 50 ▾
1 Filter by status, category, when & sort·2 View opens the full detail modal

Filters across the top: a search box covering run id, client and job; Status; Category; When (Today, Last 7 days, Last 30 days); and Sort (Newest, Oldest, Client). Clear resets everything, and Rows sets the page size.

The columns are:

ColumnWhat it shows
RunThe first eight characters of the run id; hover for the full value.
Client and JobThe client code and job name recorded on the run.
CategoryThe channel that triggered it.
StatusThe run's outcome, colour-coded.
FilesFiles succeeded over files attempted, with a red ✗n when any failed.
Started and DurStart timestamp and elapsed duration.
ActionsView opens the full detail modal: the step timeline plus per-file outcomes, where is success, amber is unconfirmed and is failed.

The engine writes two records for a run (a queued stub and the worker's final entry). The console collapses them into a single row, keeping the more complete one, so one line always means one run.

📜 Run categories — where a run came from

Logs & History

Every run is labelled with the channel that triggered it, shown as a coloured badge in Transfer Logs, Job History and Active Jobs.

Category filter — run categories1
User BasedA person in the client portal moved files themselves. Scheduled BasedThe engine's scheduler fired the job (retries show as Schedule Retry). Manual BasedAn admin pressed Run Now in this console. API BasedA machine calling the gateway REST API with a key. MCP BasedAn AI assistant working through the MCP connector. MI BasedAn Azure workload authenticating with a Managed Identity.2
1 The badge names who or what started the run·2 The Category filter lists these channels, each its own colour
CategoryWho or what started the run
User BasedA person acting in the client portal, uploading or downloading files themselves.
API BasedA machine calling the gateway's REST surface with an API key from an allowed IP address.
Schedule BasedThe engine's own scheduler firing a job at its configured time. A retry attempt from the job's retry window shows as Schedule Retry.
Manual BasedAn admin pressing Run Now in this console — same job, same engine path, but hand-triggered, so it is labelled separately from the schedule.
MCP BasedAn AI assistant working through the MCP connector.

A sixth badge, MI Based, appears for callers authenticating with a Managed Identity, and the Category filter includes it.

On the Jobs & Schedules table the badge reflects how the job itself is classified rather than a specific run, so a scheduled job shows Schedule Based before it has ever run.

For the precise origin of one run, open Job History → Status → View. The connection-flow view names the exact route or tool that invoked it — for example POST /api/v1/transfer or MCP tool: download_files — along with the caller identity where one was recorded.

📜 Run statuses, and why "unconfirmed" is not a failure

Logs & History

The statuses

Statuses you will see, and what each means:

Status filter — run statuses1
queuedAccepted; the worker has not started it yet. completedEvery expected file transferred. partialSome files transferred, some failed. failedThe run did not deliver. unconfirmedDelivery could not be proven either way — not a failure. Verify before re-sending.2
1 Every run is colour-coded by outcome·2 Amber "unconfirmed" means unknown, never green, never a failure
StatusMeaning
queuedAccepted, but the background worker has not started it. In progress, not delivered.
delivering / runningThe engine is actively transferring. Active Jobs shows this as a live progress bar; machine callers polling the run-status API see delivering.
completedEvery expected file transferred.
partialSome files transferred and some failed. Read the per-file list before deciding anything.
failedThe run did not deliver.
cancelledAn operator stopped it. Files already delivered remain on the remote; the rest are marked not sent.
unconfirmedTreat this one carefully — see below.

Why "unconfirmed" is not a failure

Unconfirmed means delivery could not be proven either way. The caller's request was cut off (around the 230-second limit) while the engine was still sending, so the outcome is genuinely unknown. It is deliberately neither success nor failure: it renders amber, like partial, and never green. Counts treat unconfirmed files as neither succeeded nor failed, so such a run can read 0/1; in the detail view the file carries an amber , not a red cross.

⚠️ Do not simply re-send an unconfirmed run. The file may already be sitting on the client's side, and re-sending delivers it twice. Verify first — check the archive and the remote directory for that file, for instance from Folder Management — and only then decide.

📜 Job History and reading a run's step log

Logs & History

Job History gives one row per run with richer per-run detail than Transfer Logs. Columns are Run #, Client Name (the friendly name, not just the code), Job Name, Category, Status, Files, Connection, Start Time and Run Time, with the same search, Status, Category, Sort and Refresh controls.

Run #Client NameJob NameCategoryStatusFilesConnection
e5f6a7b8Acme CorpHourly_FeedScheduled Basedpartial View13/32 ViewView
Transfer details — e5f6a7b8 · Connection flow
1Origin — Scheduled Based · Scheduler (internal cron)
2Source — conn-a · SFTP sftp.example.com
3Transfer — pulled Nightly_Report.csv (7.4 MB)
4Destination — conn-b · push failed: permission denied2
1 Status → View renders the run as a numbered connection flow·2 The failed stage shows in red

Three drill-ins open from the row:

  • Status → View renders the run as a connection flow, numbered top to bottom: Origin (which interface called it, and the caller), Source endpoint, Transfer (files and bytes moved, duration), Destination endpoint, and Result. When a run failed, the stage that failed is shown in red, so you can see at a glance whether the problem was reaching the source, the transfer itself, or delivery.
  • Files → View lists each file with its status, size and error text. Each file has its own View, giving a timeline scoped to that one file, including the encrypt or decrypt step if one was used.
  • Connection → View shows the connection detail the run used.

For the raw step-by-step log, use Transfer Logs and click View on a run. Each line of the timeline shows the time, a green or red , the step name and the engine's message, with per-file outcomes listed underneath.

💡 Steps recorded with an error status are the failures — read those messages first rather than the whole timeline, and check the status before you conclude anything about delivery.

🗂 Folder Management — browsing, creating and permanently deleting

Storage

Folder Management browses the storage containers behind your clients. The work is performed by the engine, not your browser, and only client containers are offered — the engine allow-lists them from the clients table, so platform containers are neither listed nor reachable.

Client container:ACME ▾↻ Refresh➕ New folder
2 file(s) selected — deletion is permanent🗑 Delete this folder🗑 Delete selected2
ACME / reports / 20261
NameSizeModified
📁 archive
📄 Nightly_Report.csv7.4 MB2026-08-18 02:00
📄 report_prev.csv7.1 MB2026-08-17 02:00
type "Delete" to confirm — one confirmation covers every ticked file
1 Breadcrumb — every segment is clickable·2 Delete selected is permanent — no undo

Browsing

Pick a container from the Client container dropdown; ↻ Refresh re-reads the list. The breadcrumb above the listing shows where you are and every segment is clickable. Folders appear with a 📁 and open on click; files show a 📄 with Size and Modified, and each has a checkbox. The header checkbox selects every file in the current folder.

➕ New folder prompts for a single name — no slashes — and creates it inside the folder you are viewing.

Deleting

Deletion is permanent. There is no soft-delete and no recycle bin on this account, so there is no undo and no recovery.

⚠️ Nothing deleted here can be recovered — and deleting a client destroys its whole container the same way.
  • 🗑 Delete selected removes the ticked files after you type the word Delete in the

confirmation box — one confirmation covers every ticked file.

  • 🗑 Delete this folder removes the folder and everything inside it. It is hidden at the container root, and you must type the folder's name to confirm. A wrong name deletes nothing.

Because a recursive delete is not atomic, both report per-item results: how many were deleted and, individually, anything that failed. Every create and delete is recorded against your admin account in System Changes.

👤 Portal users

Portal Users

Portal users are the people who sign into the client portal. The console page 👤 Portal users is the registry the gateway checks: after someone authenticates with their Microsoft account (Entra, with MFA), the gateway confirms they are in this list and enabled, then shows them only the clients they are assigned to. If they are missing or disabled, access is denied.

+ Add user1
🔍 Search email / nameStatus: All ▾Assigned: Any ▾Sort: Default ▾Clear
UserStatusAssigned clientsLast sign-inAction
jane@example.comenabled MCP2ACME
🤖 MCPAssign clientsDisableDelete
sam@example.comdisabledACME Globex3d ago
🤖 MCPAssign clientsEnableDelete
1 + Add user searches your Entra directory live·2 only enabled users can sign in; MCP badge shows AI access is on

To add someone, click + Add user. Start typing in the Display name field — it searches your Entra directory live (two characters minimum) — then pick the person from the dropdown. Email, First name and Last name fill in from the directory and cannot be typed over, so the account you grant is always a real directory identity. Tick the clients under Assign to clients, leave Enabled (can sign in immediately) on unless you want to stage the account, then click Add user. No password is ever set here.

Each row offers Assign clients, Disable (or Enable), and Delete. Assignments are edited any time through Assign clients, which is also where that person's notification preferences live. You do not need to tidy up after removing a client: deleting a client automatically prunes it from every portal user's assignments.

To let someone drive the portal from an AI client instead of the browser, see MCP access for a portal user.

👤 MCP access for a portal user

Portal Users

A portal user can connect an AI (MCP) client instead of using the browser. This is granted per user, not per client — it moved off the client record on 2026-08-05.

MCP access
Jane Roe — jane@example.com
✓ Enable MCP — let this portal user connect an AI (MCP) client
1
Tool policy
○ All tools
◉ Only the tools selected below
2
🔎 Filter tools…
Select all0 of 3 selected
list_transfers
start_transfer write
list_clients always
CancelSave
1 tick Enable MCP to reveal the policy·2 allow-list opens empty — add only what is needed

On the Portal users row, click 🤖 MCP. In the MCP access modal, tick Enable MCP, then set the Tool policy:

  • All tools — everything the gateway implements.
  • Only the tools selected below — an explicit allow-list. Use the filter box to narrow the list, and Select all to tick everything currently visible. Tools that change something are badged write; a few (such as list_clients and the help tools) are marked always and cannot be unticked, because the gateway answers them before the policy check.

Enabled users show an MCP badge with their tool count.

💡 A newly enabled account opens on Only the tools selected below with nothing ticked, so access starts at zero and you add only what is needed. Save refuses an empty selection.

Two other gates still apply and are unchanged: the client must carry the mcp access type (Clients → Edit), and the user must be assigned that client. Authorization is re-checked on every request, so turning Enable MCP off, or disabling the account, stops access on the next call.

The console-side equivalent for admins is Admin MCP access.

👤 Notification preferences for a portal user

Portal Users

Transfer notifications are opt-in per person, and you set them where you set assignments. Open Assign clients on the user's row. Under each client you tick, two checkboxes appear:

Assign clients
Jane Roe — jane@example.com
✓ Acme Corp ACME
1
✓ Email on failed attempts
Email on successful attempts
2
Globex Inc Globex
Email on failed attempts
Email on successful attempts
CancelSave
1 assign a client to reveal its toggles·2 boxes are greyed out until the client is assigned
  • Email on failed attempts
  • Email on successful attempts

Tick what that person should receive, then Save. The checkboxes are greyed out until the client above them is assigned — a user cannot be notified about a client they cannot see.

Be aware of how this is stored today: the per-client boxes are combined into two per-user preferences, one for success and one for failure. Ticking success for any assigned client opts the user into success notifications, and the same applies to failure. Treat the setting as "does this person want success and/or failure mail", not as a per-client matrix.

The subject and body templates the engine sends are authored under Global Settings → 📨 Email Notifications, and each client chooses which template it uses in Clients → Edit → Notifications.

These per-user opt-ins are now the only thing that mails a person automatically on a transfer. The global Failure notification recipients list — the ops firehose, which mailed a fixed set of addresses on every failed run regardless of client — was removed in August 2026. Ops-wide failure mail is a Trigger rule instead: Transfer failed or partial with a Send an email action.

👥 Console admin users

Admin Users

Admin users are separate from portal users: these are the accounts that sign into the admin console itself. Manage them on the 👥 Users page, in the Portal admins card.

👥 Portal admins
+ Add Entra user+ Add local user1
Display nameUsername / emailTypeStatusLast sign-inAction
Jane Roe (SSO Admin)jane@example.comEntra MCPactive8/18/26, 11:30 AM🤖 MCPDisableDelete
svc-reportsvc-reportLocalactive3d agoDisableDelete
🔒 sgadmin (break-glass) sits outside this list — cannot be edited or deleted here2
1 only sgadmin may add local users·2 sgadmin lives outside the list

There are two kinds:

  • Entra — a directory account that signs in with Microsoft single sign-on. Click + Add Entra user, search the directory, and pick the person. They are added immediately; no password exists.
  • Local — a username and password stored by the platform, used through Local Login on the sign-in screen. Click + Add local user and supply a display name, username, and a password of at least eight characters, entered twice.

Who may add whom is enforced by the buttons. Only the super admin (sgadmin) can add local users; SSO admins and the super admin can add Entra users. Each row shows the type, status, and last sign-in, with Disable/Enable and Delete available on other people's rows but never your own.

sgadmin is the break-glass account. It lives outside this list, cannot be edited or deleted here, and its password is managed in configuration rather than in the console.

Local passwords are changed with ✎ Edit password on the row — available to sgadmin for any local account, or to a local admin for their own. A signed-in local admin can also use 🔑 Change password in the profile menu.

An Entra admin can also drive the console from an AI client — see Admin MCP access.

👥 Admin MCP access

Admin Users

An SSO admin can drive the console from an AI (MCP) client instead of the browser. On the Users page, the 🤖 MCP button appears on Entra rows only — including your own row, since enabling it for yourself is a normal case. Local accounts, and sgadmin, can never use MCP: no flag exists for them and no button is shown.

MCP access
Jane Roe (SSO Admin) — jane@example.com
✓ Enable MCP — let this SSO admin connect an AI (MCP) client with console rights
Tool policy
○ All tools (full console parity)
◉ Only the tools selected below
1
🔎 Filter tools…
Select all0 of 99 selected
list_clients always
update_client write
Nothing selected → access begins at zero2
CancelSave
1 choose All tools or an explicit allow-list·2 a fresh account opens with nothing selected

The modal is the same shape as the portal-user one. Tick Enable MCP, then choose All tools (full console parity) or Only the tools selected below, using the filter and Select all to build the list. A freshly enabled account opens with nothing selected, so access begins at zero and you grant deliberately. Accounts with it on carry an MCP badge showing the tool count.

Understand what this grants: the tools are the console's own code paths — clients, connections, jobs and runs, API keys, portal and admin users, managed identities, engine and email configuration, IP bans, audit trails.

⚠️ Treat a connected session as full production admin access.

Nothing is hidden, though: every write is recorded in System Changes, attributed to the signed-in admin with the same field-level detail a console click produces, and the MCP sign-in itself appears in Login Attempts. Authorization is re-checked per request, so revoking takes effect on the next call.

🔑 API keys

Machine Access

API keys let a client's system call the gateway directly, with no human signing in. Manage them on the 🔑 API Keys page. Keys are per client, and the list is driven by each client's access types: a client can only hold a key once the api type is turned on in Clients → Edit, and rows without it say so instead of offering a key.

🔑 API keys (machine access)
🔍 Search clientKey: Any ▾Sort: Default ▾Clear
ClientKeyAllowed IPs / CIDRsCreated / RotatedAction
ACMEactive · sk_live_••••4f2a203.0.113.0/24 Edit IPs2026-08-14RotateRevoke2
Globexno key yet— none —Generate key + IPs
🔑 The generated key is shown once — only a hash is stored1
1 copy the key once — it can never be displayed again·2 Revoke asks you to type Delete to confirm

For a client with no key, click Generate key + IPs. Enter at least one entry in Allowed IPs / CIDRs — the allowlist is mandatory, and the gateway rejects calls from anywhere else with a not_allowlist reason code — then click Generate key.

The key is then shown once. Copy it with the 📋 Copy button and hand it over through a safe channel.

⚠️ Only a hash is stored, so the key can never be displayed again. If it is lost, rotate to issue a new one.

Existing rows show the status and a masked key, the allowlist, and the created or rotated date, with three actions:

ActionWhat it does
Edit IPsChanges the allowlist; the key itself is untouched.
RotateMints a replacement. The current key stops working immediately, and the new one is again shown once.
RevokeDeletes the key after you type the word Delete to confirm. That client's API calls stop.

For callers that should hold no secret at all, use Managed Identity access instead.

🔑 Managed Identity access

Machine Access

Managed Identity access is for app-to-app calls where no secret should exist at all — the alternative when an API key would have to be stored somewhere. An Azure workload — a Function App, Logic App, or a VM with a system-assigned identity — asks Azure AD for a token and presents it to the gateway. The gateway validates the token, reads its object id (the oid claim), and looks it up here. This is the machine-credential counterpart to portal users.

+ Add Managed Identity
🔍 Search name / object IDStatus: All ▾Assigned: Any ▾Sort: Default ▾Clear
Display nameObject IDTenantStatusAssigned clientsLast seenAction
Nightly Loader0000…000010000…0000enabledACME12m ago
Assign clientsEditDisableDelete2
1 the caller's Entra object id (oid) — a valid GUID·2 Edit re-keys the record and carries assignments across

Register a caller on the 🛡 Managed Identities page with + Add Managed Identity. Supply the Display name, the Object ID — the service principal's object id in Entra, which must be a valid GUID — the Tenant, an optional description, and leave Enabled ticked. Save, then use Assign clients on the new row to choose which clients that workload may act on; there are no notification preferences on this modal.

Two conditions must both hold for a call to be honoured: the identity must be present and enabled here with the target client assigned, and that client must have the Managed Identity access type turned on in Clients → Edit. If either is missing, access is denied.

Rows show the object id, tenant, status, assigned clients, and last seen. Editing lets you change the object id, which re-keys the record and carries its assignments and settings across.

⚙️ System Settings overview

Global Settings

System Settings holds organization-wide behaviour that applies across every page. Changes save immediately and take effect the next time a list paints.

📐 Page settings
Rows values: 5, 10, 25, 50, 100 · default 51
🌐 Time zone
System time zoneUTC ▾
⏱ API authorization cache
Validate-cache TTL45 s2
🖼 Admin and User portal logo
Upload logo · header & sign-in text size
🧹 Clear Log History
Login AttemptsKeep for: Never ▾Clear now3
🔒 Account Lockout & Sign-in History
Locked accounts · Banned IPs
1 Rows values used by every table·2 0 s re-checks every request (instant revocation)·3 Clear now empties the area, archived to blob first
CardWhat it controls
Page settingsThe values offered in every Rows dropdown across the application. The seeded list is 5, 10, 25, 50, 100 with a default of 5. Type a number and press Enter (or click + Add value) to add one; click the radio beside a value to make it the global default.
Time zoneEvery Run time input and timestamp display, including the engine's maintenance purge and job schedules. Default is UTC. The per-screen Show in timezone pickers remain available for cross-reference, and the system zone is excluded from their lists because it is already the default.
API authorization cacheHow long, in seconds, the gateway may trust a validated API connection before re-checking with the management API. Default 45; 0 re-checks every request, so a revoked key or identity stops working instantly. The gateway picks up a change within a few seconds.
Clear Log HistoryTwo controls per log area. Keep for is the automatic retention, in months or Never — it is the only thing that decides how long an entry is kept. Clear now empties that area completely, whatever Keep for says. Removed rows are archived as JSON to the loghistory blob container first, either way, so the data is preserved. App Logs also appears here as a read-only row — it is Application Insights telemetry, not a table, so it shows its retention for reference and has no Keep for or Clear now.
Stargate Client CredentialsThe Entra app registration the MCP connector uses to sign users in. The gateway always reads these from here, so you can rotate the secret without a redeploy. The secret is write-only and is never displayed back.

Clear Log History covers five clearable areas (in the table below), plus a read-only App Logs row described in the note after the table. Who enforces the automatic purge depends on which table it is:

AreaTableAutomatic purge runs in
Login AttemptsloginAttemptsthe management API, daily at 03:40 UTC
System ChangessystemChangesthe engine's daily maintenance
Transfer LogstransferLogsthe engine's daily maintenance
Job HistorytransferLogsthe same tablethe engine's daily maintenance
Alert StatusalertEventsthe management API, daily at 03:40 UTC

Transfer Logs and Job History are two views of one table. They are listed separately because they are separate pages, but they share a single retention value — change one and the other changes with it — and clearing either removes the same rows. The row says so, and the confirm prompt repeats it.

Never means nothing is ever purged automatically for that area; the table grows until someone runs Clear now. Alert Status and Login Attempts had no automatic purge at all before this, so they default to Never — nothing starts deleting your history because this feature arrived.

App Logs is read-only here. App Logs is Application Insights telemetry (traces, exceptions, requests) across the engine, gateway and mgmt apps — not a storage table. Ingested telemetry is immutable, so it cannot be cleared or archived; it ages out only by the App Insights retention policy, which is set in infrastructure. The App Logs row therefore shows that retention for reference and has no Keep for or Clear now control.

Keep for is the only age rule. There used to be a second dropdown here offering "older than N days" for a manual purge; it was removed, because two different age windows for one area meant the same question had two answers. Keep for decides what is kept, and Clear now is the blunt option: it deletes every row in that area regardless. The confirmation names the row count first.

⚠️ Clear now empties the area completely and cannot be undone from the console.

Locked accounts and Banned IPs also live on this page and are covered separately.

🔒 Locked accounts and unlocking an account

Security

There is no automatic lockout policy any more. Repeated failed sign-ins no longer lock an account by themselves, on any surface — not this console, not the client portal, not Gateway API keys or Managed Identities. The threshold, duration and grace-period settings were removed from System Settings in August 2026, and the behaviour behind them was switched off at the same time, so nothing is left applying an invisible policy.

🔒 Account Lockout & Sign-in History
🔒 Locked accounts
TypeIdentityFailuresStatus
Admin Portal useradmin-user5Locked1
Portal useracme-user3Counting2
🔓 Unlock selected3
🔐 Login attempts retention
Retention (disabled)90 days
1 Locked = a Trigger rule or admin locked it·2 Failures with no lock is a counter — can still sign in·3 Tick a row, then Unlock selected clears the grace period too
⚠️ An account is only locked if a Trigger rule locks it, or an administrator does. Protection is no longer built in — it depends on a rule existing and being enabled. If no rule carries the Lock the account under attack action, repeated password guessing is recorded and alerted on but never stopped.

The supplied rule for this is Brute force on one account → lock it (5 failures in 15 minutes). Check under Triggers that it is enabled before relying on it.

Failures are still counted. That has not changed and is not a leftover: the counters are the evidence every Security rule evaluates, and they are what Login Attempts and the failure column below display. What stopped is the platform deciding on its own to lock.

Locked accounts, and unlocking

The card is still titled Account Lockout Settings on System Settings. Locked accounts on it lists every identity currently locked or accumulating failures, with its type, identity, failure count and status. Tick one and click Unlock selected.

An unlock clears the lock and any remaining grace period, so sign-in works immediately.

Two things worth knowing about that list:

  • A row with a failure count but no lock time is a counter, not a lock — that identity can still sign in.
  • Deleting an admin user does not remove its row here. An orphaned counter for an account that no longer exists is harmless, but it is confusing; unlock it to clear it.

A lock only ever applies to a console admin account. The action refuses any other subject rather than writing a lock row that would block nothing — if the subject of an alert is a portal user, the rule needs Disable the portal user instead.

The sgadmin break-glass account can never be locked. It is not exempt from IP bans.

Login attempts retention also sits on this card and is disabled. It only ever trimmed the browser's copy of the page and never purged anything stored, so it is shown greyed out rather than removed — a number you can set there must not be mistaken for the one in force. The real setting is Clear Log HistoryLogin AttemptsKeep for.

🔒 Banned IPs and the self-lockout risk

Security

The Banned IPs card on System Settings blocks source IP addresses. A banned address is refused before any credential check.

🚫 Banned IPs
IP address (e.g. 203.0.113.45)Reason (optional)🚫 Ban IP1
IP addressReasonBanned byWhen
198.51.100.24Password sprayTrigger rule09:14Unban
203.0.113.9Manualadmin-user08:02Unban2
⚠ A ban is refused before any credential check — banning your own egress locks you out of the console3
1 Refused before any credential check·2 Unban takes effect immediately·3 No in-app recovery from a self-lockout

Read this before you ban anything: a ban applies platform-wide — every account and every surface. It blocks this console, the client portal, the gateway's API-key and Managed Identity callers, and MCP. It also blocks the sgadmin break-glass account, which is exempt from account lockout but not from IP bans. The refusal is enforced ahead of authentication, including on the console's own login routes.

⚠️ If you ban your own egress address, everyone is locked out of the console — and the console is where you would remove the ban. There is no in-app recovery.

Removal then requires an out-of-band storage operation against the bannedIps table by someone with the right Azure role. A ban rule can catch an office egress exactly this way, so it is not hypothetical: three administrators mistyping passwords from one office address inside the rule's window is indistinguishable from a password spray. Matching is exact-IP with no CIDR support, so other locations keep working and a locked-out admin can often just switch networks.

Addresses arrive two ways:

  • From a Trigger rule, when one carries the Ban the source IP action — typically a Password spray or Brute force on one account rule. The platform no longer bans anything on its own: the Auto-ban after threshold was removed in August 2026 and the behaviour behind it switched off, so an address is banned only because a rule you enabled said so. A short list of addresses is never banned whatever a rule says — the engine's own outbound address and loopback — because banning those would cut off every client at once.
  • Manually, using the IP and reason fields here, or the Ban button on any public-IP row in Login Attempts, which opens a modal requiring a reason.

Banned rows in Login Attempts show the IP struck through with a link to unban it, which jumps here and points at that row's Unban button. Ban and unban both take effect immediately.

⚙️ Engine Settings

Global Settings

Engine Settings are the live operational tunables for the SFTP engine. Values are written to engineConfig and picked up by the next run.

ResetSave changes3
📦 Large transfers
Queue transfers at or above400 MB1
0 = never queue
🧹 Maintenance purge
Run time02:002
Show in timezoneUTC ▾
🐕 File Watchdog
Per-file timeout300 s
Recycle session every40 files
1 At/above this size, delivery moves to a background queue·2 Run time is in the system time zone·3 Large transfers, purge and watchdog save together
⚠️ A change here affects transfers already scheduled — treat this page as production.
GroupWhat it controls
Batch size (concurrency)How many jobs run at the same time. Set in increments of 8, range 8–240. Worker concurrency is a host-level setting, so this one takes effect on the next engine deploy rather than instantly. A higher value may exhaust outbound SFTP connections or NAT-gateway ports.
Maintenance purgeThe daily cleanup that deletes staged blobs whose names end in _failed, left behind by failed or cancelled transfers. The run time is interpreted in the system time zone; Show in timezone is display-only.
File WatchdogStall protection. Each file must transfer within the per-file timeout or it is marked failed, the session reconnects, and the run continues as partial instead of freezing. Keepalive detects a silently dropped connection, handshake timeout bounds the initial connect, and Recycle session every N files re-opens the connection before a server's per-session open limit is hit.
Large transfersAzure cuts an HTTP request at about 230 seconds. At or above the size you set here, the engine hands delivery to a background queue so the sender is acknowledged immediately and cannot mistake a timeout for a failure. Size is only a proxy for duration, so lower it for slow destinations. Applies to portal transfers; API, Managed Identity and MCP callers always deliver inline. See run statuses for what unconfirmed means.
ReaperFinalizes phantom runs that stopped reporting progress. Saved on its own button.

Batch size, purge, watchdog and large transfers save together via Save changes. Watch the effect of a change on Active Jobs, and check the engine's own state on System & Health.

⚙️ Email Notifications

Global Settings

Email Notifications holds the organization-wide email templates the engine sends.

📗 Successful transfer template
Template:Acme success ▾1+ New template🗑 Delete
Template nameAcme success
From *notify@example.com ▾2
Subject{{client}} transfer complete
Variables{{client}}{{run_id}}{{file_results_table}}
Reset to default👁 Preview3
1 Create a named template — Default is reference only·2 From must be a Send-As mailbox·3 Preview renders before you commit
SettingWhat it is for
Failure notificationsA pointer, not a setting. The Failure notification recipients box — the ops firehose, which mailed a fixed list on every failed run regardless of client — was removed in August 2026, and failure mail is a Trigger now: a Transfer failed or partial rule with a Send an email action, which chooses its own recipients and template. ⚠ Nothing mails on failure until such a rule exists and is enabled.
Successful transfer template and Failed transfer templateSent to recipients who opted in for that client under Portal users. Those per-user opt-ins still work — they are a person's own choice rather than a platform-wide default.
Email TemplateUsed by Email connections, which send transferred files as attachments, and by Email to client on the Clients page.
Attachment Size LimitThe maximum total attachment size for one Email connection message, 1–150 MB. Exceeding it rejects the send and fails the run with a size-limit error.

Each template carries a name, a From address, a subject, and an HTML body. Insert variables by clicking the chips: {{client}}, {{client_code}}, {{job_name}}, {{run_id}}, {{date}}, {{file_count}}, {{file_names}}, {{file_results_table}} and others. Preview renders the result before you commit.

Two rules matter. The Default entry on each card is a reference template only — it holds the factory text that Reset to default reverts to and cannot be selected anywhere for real sending, so create at least one named template per side with + New template. Each client then picks the template it uses in Clients → Edit → Notifications.

Second, From is required on every non-Default template and is populated from the Mailbox Recipient card. The mailbox must be marked Send As, which adds it to the Send-As group.

⚠️ A From address outside the Send-As group is rejected by Exchange and the notification fails to send.

⚙️ Holiday API

Global Settings

The Holiday API page configures a public-holiday source so scheduled jobs can skip runs that land on a holiday. The engine fetches the year's list once and caches it; each scheduled job then independently toggles Skip on holidays. Configuring the source here does not change any job on its own.

ResetSave changes3
📡 API endpoint
Endpoint URL…/PublicHolidays/{year}/{country}1
Country / regionUS ▾
Year2026 ▾
Optional auth headerfor paid APIs
🔌 Test & fetch holidays2✓ 11 holidays fetched
1 Use {year} and {country} substitutions·2 Test calls the endpoint and reveals the Holidays list·3 Save changes commits the fetched list

Under API endpoint, give the URL the engine calls. Use {year} and {country} as substitutions — the free public option is https://date.nager.at/api/v3/PublicHolidays/{year}/{country}. The response must be a JSON array of objects each carrying at least a date in ISO-8601 form and a name. Pick the Country / region and Year, and add an Optional auth header if you are using a paid API.

Click Test & fetch holidays to call the endpoint. On success the Filters and Holidays cards appear so you can review the list that jobs will actually match against.

Global holidays only trims the list before it reaches the engine. With the toggle on, regional and state-specific entries — those the API marks global: false — are excluded. APIs that do not report regional scope are treated as global. The filter applies to both the preview and the saved list.

💡 A successful test does not persist anything. You must click Save changes to commit the endpoint, filters and fetched list; otherwise the configuration is lost on reload.

⚙️ SharePoint

Global Settings

The SharePoint page holds the organization-wide SharePoint configuration. The tenant URL anchors every client's SharePoint connections, and named site entries are reused across jobs so nobody retypes paths.

🏢 Tenant SharePoint URL
Tenant URLhttps://contoso.sharepoint.com/sites/1
CancelSave
📁 SharePoint sites
📁 Browse to select a siteNo site selected ➜+ Add site2
Full URL: <tenant>/sites/<name> · each site is write-tested before it is added3
1 Tenant URL must end with /sites/·2 + Add site commits the browsed selection·3 A failed write-test needs write access granted to the engine

Start with Tenant SharePoint URL — the organization's entry point, which must end with /sites/. Do not put a specific site path here; sites are added below. Save it before adding anything, or the sites card warns you that the tenant is missing.

Under SharePoint sites, click Browse to select a site to pick from the tenant. Sites you have already added are hidden from the browser. Your selection appears as pending; click + Add site to commit it. The full URL is built as <tenant>/sites/<name>.

Adding is not just a list entry — every selected site is write-tested first. The engine attempts to create and then delete a test file in that site's default document library, using its own identity and the same path a job would use. Sites that pass are added.

If any site fails the probe, a Some sites can't be added dialog lists each result with its error. From there you can Modify selection to go back and adjust, or Proceed with the sites that succeeded. A site that fails cannot be used until write access is granted to the engine in SharePoint.

💡 Verifying up front is deliberate — it surfaces a permissions gap here rather than as a failed transfer later, in a job run.

🔒 Reading the Login Attempts trail

Security

Login Attempts is the authentication audit across the whole platform. It spans Admin Portal sign-ins, Gateway API-key and Managed-Identity checks, and Client Portal Entra sign-ins, so it is the single place to answer "did this identity get in, and from where".

Surface: All ▾ Result: All ▾ Reason: bad_password ▾1 When: Last 24h ▾ 🔍 Search identity / IP / city
WhenSurfaceIdentityResultSource IP
09:14Client Portalacme-usersuccess198.51.100.7View
09:12Gatewaykey-77failure2203.0.113.9Ban3
09:08Admin Portaladmin-userfailure203.0.113.9View
1 Reason filter is the fastest way to diagnose a failure·2 Amber ⚠ = the suspicious-detector flagged it·3 Public-IP rows carry a Ban button

Each row shows when, the surface, the identity, the user, the result, the source IP with geolocation, and the login technique. Hover a timestamp for the full UTC value. Click View for the complete event payload — geolocation, user agent, session id and the suspicious-detector verdict. Flagged rows carry an amber warning badge.

Filter by Surface (Admin Portal, Gateway, Client Portal), Result (success, failure, sign-out), time window, and sort order. The Reason filter is the fastest way to diagnose a failure, and the codes are deliberately distinct:

ReasonMeaning
bad_password, unknown_user, account_disabled, account_lockedCredential and account state. account_locked means a Trigger rule or an administrator locked that account — there is no automatic lockout policy any more.
wrong_methodThe identity tried a sign-in path it is not configured for.
not_allowlist, ip_blockedThe source IP missed a per-key allow-list.
ip_banned, brute_force_bannedThe platform-wide ban list refused the request. A different control from the allow-list codes above.
bad_key, bad_token, expired, tool_blockedMachine and MCP caller problems.

Public-IP rows carry a Ban button. Rows from an already-banned IP show the address struck through with a link to unban it. Because the ban gate runs ahead of authentication, a banned attempt records the identity the caller claimed, not one it proved.

🔒 Reading the System Changes trail

Security

System Changes is the configuration and destructive-action audit trail: who changed what, when, and from what value to what value. It is distinct from Login Attempts, which covers authentication, and from Transfer Logs, which cover data movement.

Category: All ▾ Result: All ▾ 🔍 Search actor / target / summary… ⤓ Export CSV
WhenActorCategoryActionTargetResult
11:02admin-user1SystemUpdateEngine settings2success
10:55mcp:connectorSystemBan IPBan listdenied3
1 Actor is the authenticated caller, not client-supplied·2 View shows a field-level From → To diff·3 Denied and failed attempts are recorded too

Coverage spans clients, connections, jobs, portal and admin users, API keys, Managed Identities, MCP policy and credentials, email templates and Send-As, engine settings, system settings, security settings including the ban list and every lock or ban a Trigger rule performs, console run-control such as cancel and dismiss, and folder deletions. Successful, denied and failed attempts are all recorded — a blocked action leaves a trail too.

Each row gives the timestamp, actor, category, action, target, client and result. Click Details for the full entry: the source IP with its geolocation, and a field-level Field / From / To diff of exactly what changed. Secrets and key material are redacted. A successful update that changed nothing is deliberately not recorded, so every row you see represents a real change.

Filter by search text, category, result and time window, and use Export CSV to download the filtered rows.

Importantly, this is a server-side trail, not a console feature. Actions arriving through the MCP connector, the machine REST API, or a Managed Identity caller are recorded here identically to actions you take in the console, with the actor derived from the authenticated caller rather than anything the client supplied. Retention is set under System SettingsClear Log HistorySystem ChangesKeep for. There used to be a separate Audit Retention card for this; it was removed because the same value appeared in two places.

📈 System and Health

Monitoring

System & Health reports the state of the managed systems and the resources they share. Every status pill is populated from the platform's own health check rather than being hard-coded, so a card that shows no status simply has not loaded yet. Click any card to open its detail panel.

⚙️ SFTP Engine1
Schedulerhealthy
queue 0 · failures (24h) 0
🚪 Gateway API
Statushealthy
🗂 Storage
Statushealthy
🔐 Key Vault
Statushealthy2
✉️ Notifier & Telemetry
Statushealthy
🔑 Access Channels
Statusready
Every pill comes from the platform's own health check — click a card for its detail panel3
1 Check the Engine card first when jobs are not firing·2 A blank pill just has not loaded yet·3 Click a card to open its detail panel

The view covers:

CardWhat it reports
SFTP EngineThe engine Function App, with scheduler state, queue depth, time since the last execution, and failure count over the past 24 hours. This is the first card to check when jobs are not firing.
Gateway APIThe public, rate-limited, audited front door, with requests today and authentication failures.
StorageThe platform storage account, showing client blob containers, the transfer and notification queues, and the core tables.
Key VaultWhere SFTP credentials and API-key hashes live, with its authorization model.
Notifier & TelemetryThe queue-driven notifier that sends mail through Graph using a managed identity, plus the Application Insights instance receiving traces from every app. Check this card when notifications stop arriving.
Access ChannelsThe external caller channels validated at the gateway: API keys, Managed Identities and MCP.
MCP ConnectorThe AI-assistant connector and its Entra OAuth broker endpoint.

Health tells you whether the platform is up. For a specific run's outcome, use Active Jobs, Job History or Transfer Logs instead — a healthy engine and a failed transfer are entirely compatible.

The SFTP Engine and Gateway API cards each open a detail panel with Start / Restart / Stop controls (super-admin only); the engine's panel also edits its runtime settings. For a live, cross-app log stream — errors, warnings and restarts across all three apps — see App Logs.

📈 App Logs

Monitoring

App Logs (under Engine in the sidebar) is a live application-log view drawn from Application Insights across all three platform apps — the engine, the gateway and the management API — newest first. It is one place to see errors, warnings, informational traces and app restarts, rather than looking per app.

Each row shows the time, which app emitted it, the level, and the message. Filter by app, by level, by time range (1 hour to 7 days), or type in the search box to match the message text. It opens on the full stream — all levels, last 24 hours.

All apps ▾ All levels ▾1 Last 24h ▾ 🔎 search message… 🔄 Refresh
TimeAppLevelMessage
14:02:11engineerrorTransfer failed: connection reset
14:01:50mgmtinfo 👤Restart requested2
13:58:03gatewayinfoHost started
← PrevPage 1 of 100Next → Rows 5 ▾3
1 Filter by app, level, time range or search text· 2 👤 = an operator action, ⚠ = an exception· 3 Rows-per-page comes from Page Settings
  • A 👤 marks an operator power action — a Start, Stop or Restart someone triggered from System and Health; the message names who.
  • A ⚠ marks an exception.
  • A Host started entry is the app coming up — either a routine cold start (the apps scale to zero when idle) or a restart.
💡 The list shows the newest 500 entries in the window; if it says truncated, narrow the time range or add a search term to reach older ones.
⚠️ Traces are sampled under heavy load (errors and requests are kept), so App Logs shows all un-sampled telemetry — a rich sample, not a guaranteed every-line record. If App Insights is unreachable the view degrades to just the operator power actions and says so.

How long this telemetry is kept is shown read-only on the App Logs row under System Settings → Clear Log History — it is an infrastructure retention policy, not a console setting.

📈 Starting, stopping and restarting the engine

Monitoring

Open the SFTP Engine card in System and Health for the engine's power controls and runtime settings. These are super-admin only — other admins see a lock note instead.

⚙️ SFTP Engine
Statusrunning
Power
StartRestartStop1
Runtime settings
Function timeout10 min2
Queue visibility timeout15 min
Batch size4
New batch threshold2
CloseSave settings3
1 Stop halts scheduled and in-flight transfers·2 Four tunables, live, no redeploy·3 Saving restarts the engine to apply

Power

  • Start / Stop — Stop halts all scheduled and in-flight transfers until you Start it again.
  • Restart — bounces the engine, which also re-reads its runtime settings on the way back up. Any in-flight transfer is interrupted and will redeliver.

Runtime settings

The same panel edits four engine tunables live, with no redeploy — they are stored as Azure app-setting overrides, and saving restarts the engine to apply them:

SettingWhat it controlsBounds
Function timeoutHow long a single run may take before the platform kills it1–60 minutes
Queue visibility timeoutHow long a queued transfer stays hidden while it is processedmust be ≥ the function timeout
Batch sizeHow many transfers the engine runs at once1–8
New batch thresholdConcurrency headroom above the batchbatch size + threshold ≤ 12
⚠️ A batch size above 8 risks an out-of-memory restart of the engine — the bounds are enforced on save.

This is distinct from Engine Settings under Global Settings, which holds the engine's operational config (maintenance purge, watchdog, large-transfer threshold). Power and these four host-level tunables live here on the System and Health card.

📈 Starting, stopping and restarting the gateway

Monitoring

Open the Gateway API card in System and Health for the gateway's power controls — Start, Restart and Stopsuper-admin only, mirroring the engine controls.

🚪 Gateway API
Statusrunning
Power
Start1RestartStop2
🔒 Super-admin only · no runtime-settings editor — those are engine-only3
Close
1 Start / Restart bounce the front door·2 Stop halts all portal and machine/API access·3 Every action is logged in System Changes and App Logs
  • Stop halts all client portal and machine/API access until you Start it again.
  • Restart bounces the gateway; in-flight portal and API requests are interrupted.

The gateway has no runtime-settings editor — those tunables are engine-only. Every gateway power action is recorded in System Changes and appears in App Logs tagged 👤.

🛟 Common problems

Troubleshooting

A transfer failed or shows unconfirmed

Open the run in Job History or Transfer Logs and read the step detail and per-file results before re-sending anything. Unconfirmed is its own state and is neither a success nor a failure: Azure cut the caller's HTTP request at around 230 seconds while the engine was still sending, so delivery could not be confirmed.

Run #Client NameJob NameStatus
#4471Acme CorpNightly_Reportfailed1
Step log — run #4471
1 Connect
2 Upload report.csv✗ connection reset2
Check the client's Archive folder before re-sending3
1 Open the run in Job History or Transfer Logs·2 The ✗ step and per-file result name the cause·3 Archive check avoids a duplicate send
⚠️ Check the client's Archive folder before re-sending, or they may receive the file twice.

If runs of that size keep going unconfirmed, lower the Queue transfers at or above threshold in Engine Settings so delivery moves to the background queue. A run stuck at running with no progress is finalized automatically by the reaper within roughly 15 minutes. A run that finished as partial usually means the File Watchdog bounded a stalled file — the per-file error names it.

A client cannot sign into the portal

Go to Login Attempts, filter Surface to Client Portal, and read the Reason on their failed row. account_locked means a Trigger rule or an administrator locked the account — unlock them from the Locked accounts panel on System Settings, which also clears any grace period. ip_banned means their source address is on the ban list, which blocks every surface. account_disabled is a portal-user state, not a lockout.

The console looks stale after a deploy

This is a single-page app and nav does not reload it, so an open tab keeps running the old JavaScript indefinitely. Hard-refresh with Ctrl+Shift+R before investigating any suspected regression — stale tabs have repeatedly looked like bugs.

Who to contact

Escalate to whichever inbox your Transfer failed or partial Trigger rule mails — that rule is what notifies on failure now, and its recipients are set on its Send an email action. Check the rule is enabled before assuming a silent failure went to somebody: the old global Failure notification recipients list no longer sends anything.

📄 Rules and how they are evaluated

Triggers

Triggers (under Configuration) is where you author the conditions that raise an alert. It has three tabs: Rules (what to watch), Alerts (the template those alerts are announced with) and Actions (what to do about them). A rule is a saved question the platform asks on a timer — it reads the trails the platform already keeps (Login Attempts, Transfer Logs, System Changes) and raises an alert when what it finds matches the rule. Nothing here changes how the platform behaves; a rule only watches.

New rule
✓ Enabled
What to watch *Security (8) ▾   Password spray (one IP, many accounts) ▾1
One source address failing against several accounts — evades lockout and the auto-ban.
Rule name *e.g. Password spray on the admin console
Severitywarning — act today ▾2
Re-notify while still openEvery 24 hours ▾
ScopeAll clients ▾
Window (minutes)15
Cancel🧪 Test (dry run)Save
1 Category then type — Security, Transfers or Configuration·2 Severity only labels the alert — never whether it fires

Every rule has a name, a severity (Critical, Warning or Info — this only affects how the alert is labelled and titled, never whether it fires), a type from the catalogue below, a scope, and whatever parameters the chosen type needs.

Types are grouped into three families:

  • Security — read from the authentication trail. Password spray (one IP, many accounts), Brute force on one account, Distributed attempt (many IPs, one account), Success after repeated failures, Platform blocked someone (a ban or lockout was applied), Machine credentials rejected (API key, Managed Identity or MCP), and the two break-glass types below.

The break-glass account has two rule types of its own. sgadmin is exempt from account lockout, absent from the Users list, and cannot be edited or removed — so its use deserves to be noticed rather than merely recorded.

  • Local super admin login access (sgadmin) — fires when sgadmin signs in to the admin console. Successful sign-ins only by default; tick Also alert on failed sgadmin sign-ins to catch someone guessing that password. Nothing rate-limits those attempts: sgadmin can never be locked, and there is no automatic lockout policy for any account any more.
  • Local super admin password change (sgadmin) — fires when that password is changed. It is a Key Vault reference, so the change is detected as a new version of the secret. Two limits, both inherent: it cannot say who changed it, because the change happens in Azure rather than in this application (the vault's own activity log has the actor); and a new version is not live immediately, because the Function App caches the reference, so the previous password may keep working until it refreshes. The first check after this feature was deployed adopts the current version silently — otherwise every platform would alert once for a change nobody made.
  • Transfers — read from run history. Transfer failed or partial, Transfer unconfirmed, Failure rate for a client, and Job has not succeeded recently.
  • Configuration — reads System Changes. One type, Configuration change, with a checkbox list of the thirteen actions it can watch.

Each type takes a window in minutes: the rule only considers events inside that window, so a window of 60 asks "did this happen in the last hour". Windows are capped at 30 days.

🔒 A rule created by the sgadmin super-admin account can only be deleted by sgadmin. Its Delete button is disabled for every other admin and shows a padlock; the rule can still be edited, paused and tested by anyone. This is enforced by the management API, not just hidden in the console, so it holds for the admin MCP server too. Nothing changes for rules created by any other account.

💡 A rule that never fires is usually a window problem, not a detector problem. Widen the window first and use the dry run described in scope and dry run to see what the rule can actually see.

📄 Scope, the action checkboxes, and the dry run

Triggers

A rule's scope decides which records it looks at. What to watch offers two dropdowns: the rule type on the left and the scope on the right, so the two choices that determine what a rule does sit side by side.

New rule
What to watch *Configuration (1) ▾   Configuration change ▾
ScopeAll clients ▾  ·  One client ▾  ·  One job ▾1
⚠ A client-scoped Security rule sees only credential-bearing rows — scope to All clients unless you mean one client's machine credentials.
Watch these actions
✓ Select all
✓ Client deleted
✓ Job deleted
✓ IP ban applied
✓ Log history cleared
Cancel🧪 Test (dry run)2Save
1 Scope: All clients, one client, or one job (Transfers only)·2 Test (dry run) reports matches and sends nothing
  • Everything — every record the type can read.
  • One client — only records carrying that client's code.
  • One job — only that job's runs. Offered by Transfers types only, since a security event or a configuration change does not belong to a job.

⚠ A client-scoped Security rule can only see credential-bearing surfaces — the gateway API key, the portal token, and Managed Identity. Most authentication rows carry no client code at all (measured at under 7% of rows), so a client-scoped security rule is narrower than it looks. Scope security rules to Everything unless you specifically mean one client's machine credentials. The console states this next to the scope dropdown rather than leaving you to discover it.

For the Configuration change type, the parameters include a checkbox list of every watchable action — client, connection, job, portal user, admin user, API key and managed identity deletions; storage item and folder deletions; engine and system settings changes; an IP ban; and log history being cleared. A master checkbox ticks or clears them all. Ticking everything is the catch-all; ticking one gives you a single-action rule; anything in between is allowed. Nothing ticked also means every action.

Use Dry run on a rule to see what it would have matched, without sending anything. It opens as a modal you close explicitly, and reports the matches it found in the rule's window. A dry run that reports 0 matches right now is a real answer about the window you gave it — widen the window and run it again before concluding the rule is broken.

💡 The dry run sends no email. It is the safe way to prove a rule works before anyone's inbox depends on it.

📄 What a rule does when it fires — the Action step

Triggers

Every rule has an Action step with two choices, and a rule does one or the other:

New rule
ActionAlerts — email a recipient list ▾1
A rule does one or the other. Run Task — perform saved actions runs actions from Triggers → Actions instead.
Email recipientsops@example.com, security@example.com2
Email templateGlobal Alert Notification (active) ▾
Cancel🧪 Test (dry run)Save
1 One Action dropdown — Alerts or Run Task, never both·2 Recipients are opt-in; only these addresses are told
  • Alerts — email a list of recipients using an alert template. This is what rules did before actions existed, and it is what every rule saved before then still does.
  • Run Task — perform one or more saved actions: ban the address, disable the job, take a backup, post to a webhook, and so on.

A rule set to Run Task does not also email. If you want both, add an Email a recipient list action alongside the others — that keeps "who gets told" in the same list as everything else the rule does, in the order you choose.

💡 The Webhook field that used to sit on the rule is gone. It is a POST to a webhook action now. A rule that already had one keeps working, and the editor says so — nothing was dropped.

Choosing Alerts

Recipients are opt-in: a rule sends only to the addresses it names. You pick which email template to use, from the templates managed under Triggers → Alerts (they moved there from Email Notifications, and are named Alert Templates). Templates on the alert side are separate from the transfer-notification ones, and a built-in Global Alert Notification template always exists so a rule can send before anyone authors anything. Its From address is infotech@wcminvest.com.

⚠ The From address must be a mailbox the platform is allowed to send as — it has to be in the SFTP Send-As group. A From address outside that group fails silently at the mail service; the alert is raised and recorded, but nothing arrives. If an alert shows as fired with a delivery error, check the From address first.

Templates can use {{rule_name}}, {{rule_type}}, {{severity}}, {{severity_label}}, {{subject}}, {{summary}}, {{client}}, {{client_code}}, {{first_seen}}, {{occurrences}} and {{alert_table}} — the last inserts the same detail table the built-in layout uses. A token you leave out simply does not appear; the preview lists any token that resolved to nothing so you can spot a typo before sending.

When a rule matches many events at once, three or more are grouped into one email rather than sent individually, titled with the count. That keeps a spray of fifty failed logins from becoming fifty messages.

📄 Actions — what a rule can do about it

Triggers

Triggers → Actions is where you build the responses a rule can perform. An action is saved once and reused by any number of rules.

RulesAlertsActions
+ New action2↻ Refresh1 action(s)
⚡ Actions
NameDoesActs onActiveAction
Blocklist IP addressBan the source IP ⚠the ip in the alert1activeEditDelete
1 Each action takes its target from the alert that fired·2 Build one once and reuse it across rules

An action takes its target from the alert

This is the part worth understanding. "Ban the source IP" does not mean a fixed address you typed in — it means the address in the alert that fired. The same is true of "disable the job in the alert" and "disable the client in the alert". One action therefore covers every subject a rule matches: a spray rule that finds four addresses bans those four.

If an alert does not carry the thing an action needs, the action is skipped and says so rather than acting on a guess. A configuration-change alert carries no job, so a "disable the job" action on that rule reports "this alert carries no job" and does nothing. That is the intended behaviour, not a failure.

Building one

+ New action, give it a name, and choose what it does. The list is grouped — Notify, Jobs, Clients, Credentials, Accounts, Machine access, Ops, Alert hygiene — and each choice explains itself, states what it acts on, and warns you when it changes platform state. Some actions take a parameter: recipients, a webhook URL, a note, a threshold.

Only actions the platform can actually carry out are offered. A few are defined but not yet available; the tab says how many, so the list is never quietly incomplete.

The limits, which are enforced and not advisory

  • At most five state-changing actions run per evaluation. A rule matching forty addresses will not fire forty bans; the rest are skipped and logged. That cap exists so a noisy detector cannot cause more damage than the thing it detected.
  • Some addresses are never banned — the platform's own outbound address and loopback. Banning the outbound address would cut off every client at once, so it is refused no matter what a rule says.
  • The sgadmin account is never locked. It is the break-glass account.
  • Every execution is recorded in System Changes, including the ones that were skipped and why.

Trying one safely

A rule's Test (dry run) shows exactly which actions would run, against which targets, and performs none of them. Use it before enabling a rule that changes anything.

An action can also be run on demand against a target you name, which is useful as manual remediation — banning an address yourself with the same action a rule would have used. It goes through the same guards and is audited the same way.

💡 Put Take a configuration backup first in a rule that also changes something. Actions run in the order they are listed, so the backup is taken before anything is altered.

📄 Alert Status — what has fired

Triggers

Alert Status (under Logging & History) lists alerts that have actually fired, newest first, each with the delivery result of its notification. It answers "did this rule fire, and did anyone get told".

🔍 Search rule / subject / summary…Severity: All ▾State: All ▾Clear↻ Refresh14 firing · 0 acknowledged · 32 resolved
🚨 Alert Status
WhenSeverityRuleSubjectSeenStateNotifiedSummaryAction
2m agowarningPassword spray203.0.113.9firing1View✓ Acknowledge2
1h agoinfoJob not succeededNightly_ReportresolvedsentView
1 State is firing, resolved or acknowledged·2 Acknowledge resets the counter so the next occurrence is a fresh alert

Use the search box to match on rule or subject, and the severity and state filters to narrow the list. State is firing, resolved, or acknowledged. The Rows dropdown and pager work as they do on every other list, taking their choices from Page settings on System Settings.

Each row's View button opens the alert's full summary — the rule that raised it, what it matched, when it was first seen, how many occurrences were folded into it, and whether the email was delivered.

Acknowledging an alert

A firing alert has an ✓ Acknowledge button. Acknowledging accepts the alert and resets the rule's counter for that subject, so the same condition raises a fresh alert next time instead of folding into this one and quietly incrementing a count.

⚠ That means if the condition is still happening, you should expect a new alert — and a new email — within about five minutes. That is the point of it: acknowledging says "I have seen this one", not "stop telling me". The button asks first and says so.

The row then shows acknowledged, with who did it and when. The occurrence count and everything else is kept, so the history still shows what happened. Only firing alerts offer the button: a resolved one has nothing left to reset.

💡 Acknowledging the same alert repeatedly is a signal in itself — it usually means nobody can actually fix the underlying condition. The audit trail records each one, so that pattern is visible.
💡 An alert recorded with a delivery failure is still a real alert. The condition was detected and stored here; only the notification failed. Do not treat a delivery error as a false positive.

📄 What a backup contains, and taking one

Backup & Restore

Backup & Restore (under Global Settings) captures the platform's configuration, its Key Vault secrets, and the log history behind every report. Backups are written to a private recovery container on the storage account, gzipped, one file per backup.

💾 Take a backup1↻ Refresh
🔑 Backup encryption key
key loaded EA:6A:…:7C:95 · RSA-4096
✨ Generate a new keyUpload a key⬇ Download key🗑 Clear key
💾 Backups
TakenByKindContentsSizeAction
Today 14:02adminmanualConfig + history4.2 MBRestore Download Delete2
Today 02:00schedulerscheduledConfig only0.9 MBRestore Download Delete
1 Take a backup seals a snapshot with the loaded encryption key·2 No key loaded → a backup is refused, never written unprotected

A backup covers sixteen configuration sections — clients, client connections, jobs and schedules, portal and admin users, API keys, managed identities, alert rules, email and engine configuration, the uploaded portal logo, the generated client SFTP key pairs, and the rest — plus four history sections behind the report pages. History is archived, never restored: it is there so a report can be reconstructed, not written back over a live trail.

Two sections are files rather than sets of records, so they read differently: Browse shows each file's name, type and size rather than its contents, and there is no row detail to expand.

  • Portal logo — restoring it overwrites whatever logo is current. The header and sign-in text sizes that go with the logo are ordinary settings and travel with Global settings.
  • Client SFTP key pairs (private keys) — restoring only creates keys that are missing. An existing key is never overwritten, for the same reason an existing secret is not: a key regenerated since the backup is the one the client's authorized_keys matches now, and putting an older key back would break access that currently works.

⚠ Because key pairs are included, every snapshot contains client private keys. That is deliberate — losing the storage account otherwise lost them, as the secrets section covers Key Vault only — but treat a downloaded snapshot accordingly. The file is sealed with the backup encryption key, and that key's own secrets are excluded from snapshots, so a snapshot never contains what is needed to open itself.

Take a backup offers one choice:

  • Include report history — the log history behind the report pages. Leaving it out makes a much smaller file.

Every backup — manual and scheduled alike — includes all Key Vault secrets, and the finished file is sealed with the encryption key loaded on that page. There is no passphrase to choose or remember.

No key, no backup. If no encryption key is loaded, a backup is refused rather than written unprotected — a scheduled run records the refusal in System Changes, where an alert rule can watch for it. Load a key under 🔑 Backup encryption key at the top of the page.

The key card's buttons, left to right: ✨ Generate a new key, Upload a key, ⬇ Download key, 🗑 Clear key.

Generate a new key REPLACES the current one, and every backup made with the current key stops working the moment the new one exists — those snapshots can still be downloaded and deleted, but cannot be restored or browsed unless the old key is uploaded again. The confirmation says so and points you at Download key first. Generating the very first key needs no warning: there is nothing to lose.

🗑 Clear key (confirmed by typing Clear) removes the key from the platform and from Key Vault, which is how you get back to a clean slate before uploading a different key or generating one. While no key is loaded the platform is fail-closed: backups are refused rather than written unprotected, and nothing can be restored or browsed. Existing snapshots stay in the list. The confirmation leads with that, and tells you to download a copy first — without that file the snapshots sealed with it can never be opened again.

⬇ Download key saves a copy of the loaded private key at any time. Keep one somewhere safe and off this platform: it is the only thing that can open the snapshots sealed with it, so if you rebuild from scratch you need that file before any backup is any use. Every download is recorded in System Changes with the key's fingerprint, because whoever holds the file can read every snapshot it sealed.

Importing a snapshot. The file picker on the Backups card takes a .json.gz file downloaded from this list and puts it back — useful when a snapshot was deleted, carried from another environment, or kept off-platform for a rebuild. Nothing about the file is taken on trust: its format, key fingerprint and original timestamp are read out of the file itself, a file that is not a Stargate snapshot is refused, and importing over a name already in the list is refused rather than overwriting it.

An imported snapshot follows exactly the same rule as any other. If its fingerprint does not match the loaded key, it appears in the list with Restore and Browse disabled and the row states which fingerprint it needs; Download and Delete still work. Importing cannot make a snapshot restorable that the platform has no key for.

Both lists on this page are paged. The Backups list and the Restore history each have their own Rows dropdown and pager, and both take their choices and their starting size from Page settings on System Settings — so changing the list or the default there applies here as it does everywhere else. Each list remembers its own selection while the page is open. The Rows control stays available even when everything fits on one page, so a short list can still be paged.

Scheduled and manual backups are identical in content. The old split existed because only a person could supply a passphrase; the key is now held by the platform, so a scheduled run captures exactly what a manual one does.

The Backups list shows when each was taken, by whom, whether it was manual or scheduled, what it contains, and its size. Five rows are shown at a time with a pager beyond that. Each row offers Restore, Download and delete.

Each snapshot records the fingerprint of the key that sealed it. Restore and Browse are offered only while that key is loaded; with a different key, or none, both are unavailable and the row says which fingerprint it needs. Download and Delete always work, so a snapshot you can no longer open can still be archived or cleared out.

Browse opens a snapshot read-only: each section with its record count, the rows inside it, and the names of the Key Vault secrets it holds. Secret values are never shown and cannot be requested — decryption happens on the server and only a restore uses them.

📄 Restoring, and what a restore will not do

Backup & Restore

A restore adds and overwrites what the backup contains, and never deletes anything. Anything created after the backup was taken survives it. There is no "make it look exactly like the backup" option, deliberately — that would delete live records.

♻ Restore
WhenByRestored fromSectionsAppliedFailedSafety backupAction
Today 14:05adminToday 02:00161280Today 14:05 manual1View
YesterdayadminYesterday 02:0016962Yesterday manualView
A row's Restore runs 🔍 Preview changes first — ♻ Restore selected stays disabled until a preview has run.2
1 A complete safety backup is taken before anything is applied·2 Preview writes nothing; Restore adds and overwrites, never deletes

Restoring is two steps, and the first writes nothing:

1. Preview changes reports, per section, how many records would be added, how many overwritten, how many are unchanged, and how many exist now that the backup does not contain (extra — these are left alone). The Restore button stays disabled until a preview has run.

2. Restore selected applies it, section by section.

Before applying anything, the platform takes a safety backup — a complete one, including secrets, since every backup now captures everything. That is what to restore from if the restore itself was the mistake, and it is listed with the restore record.

⚠ A job that no longer exists is recreated with a NEW id. Its past runs stay linked to the old id, so history will not follow it. Nothing can be done about that from here — run history is keyed on the id the run used.

Restoring the secrets section only creates secrets that are missing from the vault. A secret that already exists is left exactly as it is, whatever its value — so restoring an older snapshot can never replace a credential that has been rotated since and is working now. That makes a restore safe to run on a live platform, and still correct when rebuilding into an empty vault.

The Restore card lists every restore performed, newest first, with how many records were applied, how many failed, and which safety backup was taken first. Five rows at a time, with a pager beyond that.

💡 Preview is free and writes nothing. Run it even when you are sure — the extra count is the quickest way to see what a backup predates.

📄 Scheduled Backups and retention

Backup & Restore

Scheduled Backups sets an automatic backup. The evaluator checks every fifteen minutes and takes a backup when one is due, which is what allows any schedule rather than a fixed one.

🕒 Scheduled Backups
✓ Run backups automatically
How oftenDaily at a set time ▾
Time02:001
Time zoneAmerica/Los_Angeles ▾
Keep how many scheduled backups142
ContentsInclude report history
Save schedule▶ Run now
🧮 Manual Backup Retention
Copies to keep (0–500)0 (all kept)
1 Daily at a set time, or every N hours (1–168)·2 Keep N prunes the oldest; 0 keeps every one
  • Run backups automatically turns the schedule on.
  • How often is either Daily at a set time or Every N hours (1 to 168).
  • Time zone applies to the daily time. Daily runs compare local dates rather than counting 24-hour periods, so a clock change neither skips a day nor runs twice.
  • Contents — whether report history is included. Scheduled backups never include secrets.
  • Keep how many scheduled backups removes the oldest scheduled backups beyond that count. 0 keeps every one.

Run now takes a scheduled-style backup immediately, through exactly the same code path as the timer, so testing the button tests the scheduler.

Manual Backup Retention is a separate limit for the backups people take by hand. Once a new manual backup pushes the count past the number you set, the oldest are deleted first until only that many remain. 0 keeps every manual backup, and that is the default — nothing is removed automatically until you choose a number. The card shows how many manual backups exist and how many are over the limit.

⚠ Manual backups are the only ones that can hold encrypted Key Vault secrets, so a purge here can destroy something unrecoverable. Lowering the number asks for confirmation and names how many will go. Safety backups taken before a restore count as manual, so they count toward this limit too.

Every retention deletion is recorded in System Changes, naming the backup, the limit that applied, and whether it held secrets — so a backup that has vanished can still be explained afterwards.