⚙️ SFTP Engine
🚪 Gateway
Recent activity (all clients)
| Client | Job | Category | Status | Files | When |
|---|
| Code | Name | Access types | Remote connection | Status | Action |
|---|
| User | Status | Assigned clients | Last sign-in | Action |
|---|
| Display name | Object ID | Tenant | Status | Assigned clients | Last seen | Action |
|---|
| Client | Job | Source → Destination | Schedule | Last run | Active | Action |
|---|
🔑 API keys (machine access)
| Client | Key | Allowed IPs / CIDRs | Created / Rotated | Action |
|---|
| Run | Client | Job | Category | Status | Files | Started | Dur | Actions |
|---|
| Job | Category | Progress | Started | Elapsed | Action |
|---|
⚙️ SFTP Engine
🚪 Gateway API
🗂 Storage
🔐 Key Vault
✉️ Notifier & 📈 Telemetry
🔑 Access Channels
🤖 MCP Connector
| Time | App | Level | Message |
|---|---|---|---|
| Loading… | |||
🧹 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.
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
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.
🧹 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.)
🔌 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 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.
🔐 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.
| When | Surface | Identity | USER | Result | Source IP | Login Technique | Details |
|---|
| Run # | Client Name | Job Name | Category | Status | Files | Connection | Start Time | Run Time |
|---|
🔔 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.
| Rule | Type | Scope | Severity | Notifies | Last fired | Active | Action |
|---|---|---|---|---|---|---|---|
| Loading… | |||||||
🚨 Alert Templates
Used by Triggers when a rule fires. The active template here is what every rule sends with unless the rule picks a different one. Variables are alert-specific — {{alert_table}} is the evidence table (or the list of matches when several fire in one pass), and transfer variables such as {{job_name}} have no value in an alert. A rule that matches several things at once sends one grouped message through this same template.
⚡ Actions
An action is a reusable response a rule performs when it fires. An action that acts on something — an IP, a job, a client — takes it from the alert, so one action covers every subject the rule matches. If an alert does not carry that subject the action is skipped and says so, rather than acting on a guess.
| Name | Does | Acts on | Active | Action |
|---|---|---|---|---|
| Loading… | ||||
🚨 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.
| When | Severity | Rule | Subject | Seen | State | Notified | Summary | Action |
|---|---|---|---|---|---|---|---|---|
| Loading… | ||||||||
| When | Actor | Category | Action | Target | Client | Result | Details |
|---|---|---|---|---|---|---|---|
| Loading… | |||||||
📐 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.
🌐 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.
⏱ 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.
🖼 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.
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.
🔑 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).
⏱ 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.)
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.
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.
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.
| Type | Identity | Failures | Status |
|---|
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.
🚫 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.
| IP address | Reason | Banned by | When |
|---|
📧 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.
📕 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.
📎 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.
📧 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.
📬 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.
📡 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}.
🔑 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.
| Taken | By | Kind | Contents | Size | Action |
|---|---|---|---|---|---|
| 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.
| When | By | Restored from | Sections | Applied | Failed | Safety backup | Action |
|---|---|---|---|---|---|---|---|
| Loading… | |||||||
👥 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 name | Username / email | Type | Status | Last sign-in | Action |
|---|
▶🚀 Getting Started4
▶🏢 Clients4
▶📄 Tools2
▶⏱ Jobs & Schedules5
▶📜 Logs & History4
▶👤 Portal Users3
▶👥 Admin Users2
▶🔑 Machine Access2
▶⚙️ Global Settings5
▶🔒 Security4
▶📈 Monitoring4
▶🛟 Troubleshooting1
▶📄 Triggers5
▶📄 Backup & Restore3
▶🤖 AI Assistant (MCP)1
❓ Stargate Admin Console — guide
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.
🤖 Connect an 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.
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.
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
The console opens on a sign-in overlay titled Stargate Admin Portal with two choices.
- 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
sgadminsuper-admin break-glass account.sgadminsits outside the Users table, cannot be edited or removed, and its password is managed in configuration rather than in the console. Onlysgadmincan add new local accounts.
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
The left sidebar is headed 🛠 Stargate Admin Portal, with 📊 Dashboard on its own at the top and everything else in four groups.
- 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 & History — Alert 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
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.
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
The Dashboard opens on four figures across the top.
2 ✗
- 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.
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
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.
| Code | Name | Access types | Remote connection | Status | Action |
|---|---|---|---|---|---|
| ACME | Acme Corp | 👤 ⏱ 🤖 | sftp.example.com +22 | Active | Edit Delete |
| GLOBEX | Globex Ltd | 🛡 | team.example.com / Reports | Disabled | Edit Delete |
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.
🏢 Access types
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 type | What it allows |
|---|---|
portal | Assigned portal users can see this client in the client portal and move files for it. |
scheduled | The engine may run scheduled jobs for this client. Only clients with this type appear when you pick a client in the Jobs wizard. |
api | Machine 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. |
mcp | The 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. |
mi | Azure 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. |
🏢 Client connections
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.
| Type | What you configure |
|---|---|
| SFTP | Protocol (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. |
| SharePoint | The 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. |
| SendEmail | To, CC, and an Email Template that supplies the sender address. |
| ReceiveEmail | A Receive-As Mailbox Access, plus From Address and Subject Line filters. |
Each type can also enable file encryption.
🏢 Editing and deleting a client
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.
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 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
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.
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.
📄 Key Pair
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 creates a fresh Ed25519 SSH pair in your browser using Web Crypto — the same generator SFTP connections use. Download the private key as
id_ed25519and the public key asid_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
.ppkis 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
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.
| Client | Job | Source → Destination1 | Schedule | Last run | Active | Action |
|---|---|---|---|---|---|---|
| ACME | Nightly_Report | Schedule Based conn-a → conn-b | Every 40m | completed 32/322 8/18/2026, 3:57 PM | 3 | ▶ Run Now Edit Delete |
| ACME | Hourly_Feed | Schedule Based conn-a → conn-c | Cron: 0 2 * * * 📅 skip-hol | failed 0/2 8/18/2026, 2:00 PM | 🔒 off | ▶ Run Now Edit Delete |
Reading the table
The table has seven columns:
| Column | What it shows |
|---|---|
| Client | The client the job belongs to. |
| Job | The job name. |
| Source → Destination | A category badge and the real connection names. |
| Schedule | Cron: 0 2 *, Every 15m, or On request, plus a 📅 skip-hol badge when the job skips holidays. |
| Last run | The last outcome, a succeeded/total file count and a timestamp — that is the genuine last run, scheduled or manual. |
| Active | The 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
Click + New job. Required fields are marked with a red asterisk.
- 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,.gpgor.ascauto-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.
* 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
Frequency and run time
Frequency offers seven options. Interval choices run around the clock; the rest start at the Run time you set.
| Frequency | What it does |
|---|---|
| Every N Minutes | N from 1 to 1440, around the clock. |
| Hourly | Every N hours, 1 to 24, around the clock. |
| Daily | Once a day at the Run time. |
| Weekly | Pick a day; runs at the Run time. |
| WeekDay | Tick any of Mon–Fri; runs at the Run time. |
| Monthly | Month plus day of month; runs at the Run time. |
| BusinessDay | 1 = 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.
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
▶ 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.
| Client | Job | Schedule | Last run | Action |
|---|---|---|---|---|
| ACME | Nightly_Report | Every 40m | completed 32/32 | ▶ Run Now1EditDelete |
| ACME | Paused_Feed 🔒 off | Daily 02:00 | — | ▶ Run NowEditDelete |
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
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.
| Job | Category | Progress | Started | Elapsed | Action |
|---|---|---|---|---|---|
| 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 |
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.
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
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.
| Run | Client | Job | Category | Status | Files | Started | Dur | Actions |
|---|---|---|---|---|---|---|---|---|
| a1b2c3d4 | ACME | Nightly_Report | Scheduled Based | completed | 32/32 | 2026-08-18 15:25:11 | 32m 4s | View2 |
| e5f6a7b8 | ACME | Hourly_Feed | Manual Based | partial | 3/32 ✗29 | 2026-08-18 12:53:03 | 0.0s | View |
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:
| Column | What it shows |
|---|---|
| Run | The first eight characters of the run id; hover for the full value. |
| Client and Job | The client code and job name recorded on the run. |
| Category | The channel that triggered it. |
| Status | The run's outcome, colour-coded. |
| Files | Files succeeded over files attempted, with a red ✗n when any failed. |
| Started and Dur | Start timestamp and elapsed duration. |
| Actions | View 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
Every run is labelled with the channel that triggered it, shown as a coloured badge in Transfer Logs, Job History and Active Jobs.
| Category | Who or what started the run |
|---|---|
| User Based | A person acting in the client portal, uploading or downloading files themselves. |
| API Based | A machine calling the gateway's REST surface with an API key from an allowed IP address. |
| Schedule Based | The 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 Based | An admin pressing Run Now in this console — same job, same engine path, but hand-triggered, so it is labelled separately from the schedule. |
| MCP Based | An 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
The statuses
Statuses you will see, and what each means:
| Status | Meaning |
|---|---|
| queued | Accepted, but the background worker has not started it. In progress, not delivered. |
| delivering / running | The engine is actively transferring. Active Jobs shows this as a live progress bar; machine callers polling the run-status API see delivering. |
| completed | Every expected file transferred. |
| partial | Some files transferred and some failed. Read the per-file list before deciding anything. |
| failed | The run did not deliver. |
| cancelled | An operator stopped it. Files already delivered remain on the remote; the rest are marked not sent. |
| unconfirmed | Treat 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.
📜 Job History and reading a run's step log
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 Name | Job Name | Category | Status | Files | Connection |
|---|---|---|---|---|---|---|
| e5f6a7b8 | Acme Corp | Hourly_Feed | Scheduled Based | partial View1 | 3/32 View | View |
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.
🗂 Folder Management — browsing, creating and permanently deleting
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.
| Name | Size | Modified | |
|---|---|---|---|
| 📁 archive | — | — | |
| 📄 Nightly_Report.csv | 7.4 MB | 2026-08-18 02:00 | |
| 📄 report_prev.csv | 7.1 MB | 2026-08-17 02:00 |
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.
- 🗑 Delete selected removes the ticked files after you type the word
Deletein 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 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.
| User | Status | Assigned clients | Last sign-in | Action |
|---|---|---|---|---|
| jane@example.com | enabled MCP2 | ACME | — | 🤖 MCPAssign clientsDisableDelete |
| sam@example.com | disabled | ACME Globex | 3d ago | 🤖 MCPAssign clientsEnableDelete |
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
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.
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_clientsand 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.
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
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:
- 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 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.
| Display name | Username / email | Type | Status | Last sign-in | Action |
|---|---|---|---|---|---|
| Jane Roe (SSO Admin) | jane@example.com | Entra MCP | active | 8/18/26, 11:30 AM | 🤖 MCPDisableDelete |
| svc-report | svc-report | Local | active | 3d ago | DisableDelete |
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
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.
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.
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
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.
| Client | Key | Allowed IPs / CIDRs | Created / Rotated | Action |
|---|---|---|---|---|
| ACME | active · sk_live_••••4f2a | 203.0.113.0/24 Edit IPs | 2026-08-14 | RotateRevoke2 |
| Globex | no key yet | — none — | — | Generate key + IPs |
Delete to confirmFor 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.
Existing rows show the status and a masked key, the allowlist, and the created or rotated date, with three actions:
| Action | What it does |
|---|---|
| Edit IPs | Changes the allowlist; the key itself is untouched. |
| Rotate | Mints a replacement. The current key stops working immediately, and the new one is again shown once. |
| Revoke | Deletes 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
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.
| Display name | Object ID | Tenant | Status | Assigned clients | Last seen | Action |
|---|---|---|---|---|---|---|
| Nightly Loader | 0000…00001 | 0000…0000 | enabled | ACME | 12m ago | Assign clientsEditDisableDelete2 |
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
System Settings holds organization-wide behaviour that applies across every page. Changes save immediately and take effect the next time a list paints.
| Card | What it controls |
|---|---|
| Page settings | The 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 zone | Every 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 cache | How 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 History | Two 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 Credentials | The 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:
| Area | Table | Automatic purge runs in |
|---|---|---|
| Login Attempts | loginAttempts | the management API, daily at 03:40 UTC |
| System Changes | systemChanges | the engine's daily maintenance |
| Transfer Logs | transferLogs | the engine's daily maintenance |
| Job History | transferLogs — the same table | the engine's daily maintenance |
| Alert Status | alertEvents | the 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.
Locked accounts and Banned IPs also live on this page and are covered separately.
🔒 Locked accounts and unlocking an account
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.
| Type | Identity | Failures | Status | |
|---|---|---|---|---|
| Admin Portal user | admin-user | 5 | Locked1 | |
| Portal user | acme-user | 3 | Counting2 |
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 History → Login Attempts → Keep for.
🔒 Banned IPs and the self-lockout risk
The Banned IPs card on System Settings blocks source IP addresses. A banned address is refused before any credential check.
| IP address | Reason | Banned by | When | |
|---|---|---|---|---|
| 198.51.100.24 | Password spray | Trigger rule | 09:14 | Unban |
| 203.0.113.9 | Manual | admin-user | 08:02 | Unban2 |
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.
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
Engine Settings are the live operational tunables for the SFTP engine. Values are written to engineConfig and picked up by the next run.
| Group | What 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 purge | The 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 Watchdog | Stall 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 transfers | Azure 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. |
| Reaper | Finalizes 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
Email Notifications holds the organization-wide email templates the engine sends.
| Setting | What it is for |
|---|---|
| Failure notifications | A 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 template | Sent 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 Template | Used by Email connections, which send transferred files as attachments, and by Email to client on the Clients page. |
| Attachment Size Limit | The 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.
⚙️ Holiday API
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.
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.
🔒 Reading the Login Attempts trail
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".
| When | Surface | Identity | Result | Source IP | |
|---|---|---|---|---|---|
| 09:14 | Client Portal | acme-user | success | 198.51.100.7 | View |
| 09:12 | Gateway | key-77 | failure ⚠2 | 203.0.113.9 | Ban3 |
| 09:08 | Admin Portal | admin-user | failure | 203.0.113.9 | View |
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:
| Reason | Meaning |
|---|---|
bad_password, unknown_user, account_disabled, account_locked | Credential and account state. account_locked means a Trigger rule or an administrator locked that account — there is no automatic lockout policy any more. |
wrong_method | The identity tried a sign-in path it is not configured for. |
not_allowlist, ip_blocked | The source IP missed a per-key allow-list. |
ip_banned, brute_force_banned | The platform-wide ban list refused the request. A different control from the allow-list codes above. |
bad_key, bad_token, expired, tool_blocked | Machine 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
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.
| When | Actor | Category | Action | Target | Result |
|---|---|---|---|---|---|
| 11:02 | admin-user1 | System | Update | Engine settings2 | success |
| 10:55 | mcp:connector | System | Ban IP | Ban list | denied3 |
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 Settings → Clear Log History → System Changes → Keep 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
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.
The view covers:
| Card | What it reports |
|---|---|
| SFTP Engine | The 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 API | The public, rate-limited, audited front door, with requests today and authentication failures. |
| Storage | The platform storage account, showing client blob containers, the transfer and notification queues, and the core tables. |
| Key Vault | Where SFTP credentials and API-key hashes live, with its authorization model. |
| Notifier & Telemetry | The 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 Channels | The external caller channels validated at the gateway: API keys, Managed Identities and MCP. |
| MCP Connector | The 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
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.
| Time | App | Level | Message |
|---|---|---|---|
| 14:02:11 | engine | error ⚠ | Transfer failed: connection reset |
| 14:01:50 | mgmt | info 👤 | Restart requested2 |
| 13:58:03 | gateway | info | Host started |
- 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.
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
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.
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:
| Setting | What it controls | Bounds |
|---|---|---|
| Function timeout | How long a single run may take before the platform kills it | 1–60 minutes |
| Queue visibility timeout | How long a queued transfer stays hidden while it is processed | must be ≥ the function timeout |
| Batch size | How many transfers the engine runs at once | 1–8 |
| New batch threshold | Concurrency headroom above the batch | batch size + threshold ≤ 12 |
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
Open the Gateway API card in System and Health for the gateway's power controls — Start, Restart and Stop — super-admin only, mirroring the engine controls.
- 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
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 Name | Job Name | Status |
|---|---|---|---|
| #4471 | Acme Corp | Nightly_Report | failed1 |
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 (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.
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:
sgadmincan 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.
📄 Scope, the action checkboxes, and the dry run
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.
- 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.
📄 What a rule does when it fires — the Action step
Every rule has an Action step with two choices, and a rule does one or the other:
- 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.
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 → Actions is where you build the responses a rule can perform. An action is saved once and reused by any number of rules.
| Name | Does | Acts on | Active | Action |
|---|---|---|---|---|
| Blocklist IP address | Ban the source IP ⚠ | the ip in the alert1 | active | EditDelete |
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.
📄 Alert Status — what has fired
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".
| When | Severity | Rule | Subject | Seen | State | Notified | Summary | Action |
|---|---|---|---|---|---|---|---|---|
| 2m ago | warning | Password spray | 203.0.113.9 | 2× | firing1 | — | View | ✓ Acknowledge2 |
| 1h ago | info | Job not succeeded | Nightly_Report | 1× | resolved | sent | View | — |
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.
📄 What a backup contains, and taking one
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.
| Taken | By | Kind | Contents | Size | Action |
|---|---|---|---|---|---|
| Today 14:02 | admin | manual | Config + history | 4.2 MB | Restore Download Delete2 |
| Today 02:00 | scheduler | scheduled | Config only | 0.9 MB | Restore Download Delete |
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_keysmatches 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
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.
| When | By | Restored from | Sections | Applied | Failed | Safety backup | Action |
|---|---|---|---|---|---|---|---|
| Today 14:05 | admin | Today 02:00 | 16 | 128 | 0 | Today 14:05 manual1 | View |
| Yesterday | admin | Yesterday 02:00 | 16 | 96 | 2 | Yesterday manual | View |
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.
📄 Scheduled Backups and retention
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.
- 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.