Mission Control is a .NET operations system for collecting integration events and current host/service status. It accepts authenticated events, routes them through RabbitMQ, archives complete event envelopes in SQLite, projects selected GitHub activity, collects host and Docker diagnostics, and presents the results in an authenticated Blazor dashboard.
| Project | Purpose |
|---|---|
MissionControl.Gateway |
ASP.NET Core ingress for generic integration events and signed GitHub webhooks. Publishes normalized envelopes to RabbitMQ. |
MissionControl.Archive |
RabbitMQ consumer and HTTP query API backed by a SQLite event archive. |
MissionControl.Agent |
Collects host, Docker, and protocol status; persists the latest node snapshot; optionally publishes operational snapshot events; exposes a sanitized snapshot API. |
MissionControl.Dashboard |
Authenticated Blazor Server UI for archive statistics, events, node resources, containers, probes, and a configured service catalog. Also exposes the bearer-authenticated Mobile API proxy for installed clients. |
MissionControl.GitActivity |
Consumes selected GitHub push events, stores an allowed repository/branch projection in SQLite, and exposes an API-key-protected activity feed. |
MissionControl.Contracts |
Shared Agent, Archive, GitActivity, service-catalog, integration-event, and GitHub transport contracts. |
MissionControl.Client |
Shared Agent, Archive, and GitActivity HTTP clients plus client-side feed state. |
MissionControl.UI |
Razor Class Library with shared Git Activity, event details, overview, and service UI plus the shared Mission Control theme. |
MissionControl.Mobile |
.NET MAUI Blazor Hybrid application sharing the Client, Contracts, and UI projects across Windows and Android builds. |
MissionControl.Messaging.RabbitMq |
Shared RabbitMQ consumer, consumer options, and integration-event processor contract. |
MissionControl.Observability |
Shared liveness/readiness endpoint mapping and RabbitMQ connection health check. |
MissionControl.Tests |
xUnit unit and focused integration coverage for storage, messaging, Agent, Dashboard, shared client/UI behavior, GitActivity, and API contracts. |
The primary integration-event path is:
generic client or GitHub
-> MissionControl.Gateway
-> RabbitMQ topic exchange
-> MissionControl.Archive
-> SQLite event archive
-> Archive HTTP API
-> MissionControl.Dashboard
The shared cross-platform UI layers sit above those service APIs:
MissionControl.Contracts
-> MissionControl.Client
-> MissionControl.UI
-> MissionControl.Dashboard authenticated Blazor web app
-> MissionControl.Mobile Windows MAUI Blazor Hybrid app
-> MissionControl.Mobile Android MAUI Blazor Hybrid app
MissionControl.GitActivity consumes github.push.received events from its own
RabbitMQ queue. It filters configured repositories and branches, then stores a
commit-oriented SQLite projection. Dashboard and Mobile use the private service
path:
authenticated Dashboard page
-> Dashboard server-side GitActivity client with X-Mission-Control-Key
-> private GET /api/github/activity
Windows / Android MAUI client
-> HTTPS Dashboard Mobile API with bearer token
-> GET /api/mobile/git-activity
-> Dashboard server-side GitActivity client with X-Mission-Control-Key
-> private GET /api/github/activity
The Dashboard holds the GitActivity API key. Installed MAUI clients hold only the Dashboard Mobile bearer token; the proxy never returns the GitActivity key or service URL. A deployment may separately expose the API-key-protected GitActivity endpoint for trusted server-side consumers, but Mobile does not use that route.
The Agent path is separate:
host, Docker, and protocol collectors
-> AgentWorker
-> latest snapshot in Agent SQLite
-> GET /api/snapshot
-> Dashboard Overview and Services pages
Installed Windows and Android clients share the same Razor UI and service clients. Archive data is reached through Dashboard, not by exposing Archive publicly:
Windows / Android MAUI client
-> HTTPS Dashboard Mobile API with bearer token
-> internal Dashboard Archive client
-> private Archive HTTP service
The current mobile app also polls the configured Agent snapshot API for live host, container, and protocol status. Keep Archive private; do not publish the Archive HTTP service solely to support installed clients.
Every Agent collection is saved locally. A snapshot is published as an integration event only for the first successful attempt, an operational-state change, or the configured heartbeat. CPU, memory, container resource usage, probe duration, and diagnostic wording alone do not trigger publication. A failed publication remains eligible for the next collection, and successful publication metadata is recorded without preventing later snapshot persistence.
The Agent currently collects:
- logical processor count, CPU usage, and memory totals/availability from Linux
/procdata; - all Docker containers, including running, stopped, exited, created, and restarting states;
- image, restart count, and resource metrics when the Docker API supplies them;
- Echo, QOTD, Gopher, Finger, and Daytime protocol checks with per-probe timeouts.
Docker collection uses a Unix domain socket, normally /var/run/docker.sock. Docker is disabled by default on Windows. On non-Linux hosts the Agent reports logical processor count, but CPU and memory values are unavailable. A Docker outage or an individual container-statistics failure does not discard host and protocol results.
The Agent stores one latest snapshot per node rather than a metrics history. GET /api/snapshot returns that snapshot with age, staleness, Docker availability, publication status, and sanitized protocol diagnostics. The endpoint applies configured CORS origins and a fixed request rate limit. Raw exception details and local Docker socket paths are not exposed through protocol diagnostics.
The Dashboard requires an authenticated user and provides:
- Overview: archive totals and categories plus the current node CPU/memory snapshot;
- Services: service-catalog entries correlated with Agent containers and protocol probes, including uncatalogued observations;
- Events: filtering, cursor-based older-event loading, modal/full-page details, and periodic checks for new events.
- Git Activity: a bounded recent-commit feed with repository and branch filters and manual refresh.
Agent data on Overview and Services refreshes automatically. Event data also polls automatically. Freshness is recalculated locally between requests. When a later refresh fails, the last successful Agent or event data remains visible with a warning. If older events are loaded, polling preserves the current list and shows a “new events available” action instead of replacing the user’s position.
The service catalog is loaded from the required MissionControl.Dashboard/services.json file and reloads automatically. An invalid reload keeps the last valid catalog visible and displays a warning until a later valid update succeeds. Dashboard authentication uses a local SQLite user database and persisted ASP.NET Core Data Protection keys.
MissionControl.Mobile is one .NET MAUI Blazor Hybrid application. The current
project builds Android on all supported build hosts, adds iOS and Mac Catalyst
targets on non-Linux hosts, and adds the Windows target on Windows. The installed
Windows and Android applications share the same Razor UI, shared theme,
contracts, and HTTP clients.
The mobile app currently provides:
- Overview: Archive statistics through the Dashboard Mobile API, Agent snapshot status, node resources, container counts, and protocol counts;
- Services: bundled service-catalog entries correlated with Agent containers and protocol probes;
- Events: source and event-type filters, cursor-based older-event loading, manual refresh, new-event detection, event cards, modal details, and a full-page details route;
- Git Activity: the same shared bounded commit feed, loaded through the authenticated Dashboard Mobile API rather than the private service;
- Settings: entry, testing, storage, and removal of the raw Mobile API bearer token.
Event cards open an in-place details modal. The modal supports the Close button,
backdrop dismissal, Escape dismissal on desktop, and a View full page action.
The app reads the shared service catalog from its bundled services.json asset,
which is included from MissionControl.Dashboard/services.json at build time.
Agent polling runs every 30 seconds from the mobile layout, and Overview and Services also expose manual refresh for Agent data. Archive event and statistics requests keep last-known data visible when a later refresh fails and surface the failure as a warning or actionable Settings message.
For building and automated testing:
- .NET 10 SDK
- a supported .NET development platform
The test suite uses temporary SQLite databases and controlled HTTP, Docker, and protocol doubles. It does not require a running RabbitMQ broker, Docker daemon, external network service, or production credentials.
For a complete local event flow:
- RabbitMQ reachable by Gateway, Archive, and GitActivity;
- writable storage for Archive, Agent, Dashboard authentication, and GitActivity SQLite databases;
- valid API keys and, when enabled, a GitHub webhook secret;
- Archive and Agent HTTP endpoints reachable by Dashboard.
For full Agent metrics, run on Linux with permission to read /proc and access the configured Docker Unix socket. Protocol probes additionally require network access to their configured targets.
All executables use standard ASP.NET Core configuration. Environment variables replace : with __; for example, Agent__PublicationHeartbeatMinutes=5 or EventArchive__DatabaseFileName=mission-control.db. Keep credentials in user secrets, environment variables, or another secret provider rather than checked-in JSON.
Important sections are EventSources, RabbitMq, and GitHubWebhook.
{
"EventSources": {
"Sources": [
{
"Name": "example-source",
"ApiKey": "replace-with-at-least-32-characters"
}
]
},
"RabbitMq": {
"HostName": "localhost",
"Port": 5672,
"UserName": "mission-control",
"Password": "replace-with-a-secret",
"VirtualHost": "/mission-control",
"ClientProvidedName": "mission-control-gateway-local"
},
"GitHubWebhook": {
"Enabled": true,
"Secret": "replace-with-at-least-32-characters",
"AllowedOwner": "example-owner",
"MaxPayloadBytes": 5242880
}
}Event-source names and keys must be non-empty and unique; keys must contain at least 32 characters. An enabled GitHub webhook requires a secret of at least 32 characters and an allowed owner. Payload limits must be between 1 byte and 25 MB.
Archive requires RabbitMq, RabbitMqConsumer, and EventArchive settings.
{
"RabbitMq": {
"HostName": "localhost",
"Port": 5672,
"UserName": "mission-control",
"Password": "replace-with-a-secret",
"VirtualHost": "/mission-control",
"ClientProvidedName": "mission-control-archive-local"
},
"RabbitMqConsumer": {
"ExchangeName": "kgivler.events",
"QueueName": "mission-control.archive",
"RoutingKey": "#",
"PrefetchCount": 10
},
"EventArchive": {
"DatabaseFileName": "mission-control.db",
"BasePath": "Data"
}
}EventArchive and DatabaseFileName are required and validated before the RabbitMQ consumer starts. DatabaseFileName must be a filename, not a path. A missing or blank BasePath resolves to Data under the application directory; a relative value is also resolved from the application directory, while an absolute value is used directly. Invalid paths fail startup instead of falling back to another database.
Agent configuration is split across Agent, AgentApi, AgentStorage, and MissionControl.
{
"Agent": {
"NodeName": "local-node",
"IntervalSeconds": 60,
"PublicationHeartbeatMinutes": 15,
"DockerEnabled": true,
"DockerSocketPath": "/var/run/docker.sock",
"DockerTimeoutSeconds": 5,
"Probes": [
{
"Name": "example-echo",
"Host": "127.0.0.1",
"Protocol": "echo",
"Port": 7,
"TimeoutMilliseconds": 2000
}
]
},
"AgentApi": {
"StaleAfterSeconds": 180,
"AllowedOrigins": [
"https://dashboard.example.com"
]
},
"AgentStorage": {
"DatabaseFileName": "mission-control-agent.db",
"BasePath": "Data"
},
"MissionControl": {
"Enabled": false,
"BaseUrl": "http://127.0.0.1:5190",
"ApiKey": "",
"TimeoutMilliseconds": 1000
}
}Collection interval, publication heartbeat, Docker timeout, and probe timeouts must be positive. Probe ports must be between 1 and 65535. MissionControl.Enabled controls publication to the configured Mission Control destination; local snapshot persistence and the Agent API continue independently of publication suppression.
Dashboard uses upstream URLs plus Dashboard, MissionControl, and ServiceCatalog configuration.
{
"Archive": {
"BaseUrl": "http://localhost:5191/"
},
"Agent": {
"BaseUrl": "http://localhost:5194/"
},
"GitActivityApi": {
"Enabled": true,
"BaseUrl": "http://gitactivity:8080/",
"ApiKey": "replace-with-the-private-service-key"
},
"Dashboard": {
"Refresh": {
"AgentSnapshotRefreshSeconds": 30,
"EventRefreshSeconds": 30,
"SnapshotStaleAfterSeconds": 120
},
"DateTime": {
"TimeZoneId": "UTC",
"Format": "yyyy-MM-dd HH:mm:ss zzz"
},
"Authentication": {
"DatabaseFileName": "dashboard-auth.db",
"BasePath": "data",
"DataProtectionKeysPath": "data/data-protection",
"CookieLifetimeHours": 8,
"MaxFailedAttempts": 5,
"LockoutMinutes": 15
},
"MobileApi": {
"Enabled": true,
"TokenHash": "base64-encoded-sha256-token-hash"
}
},
"MissionControl": {
"Enabled": false,
"BaseUrl": "http://127.0.0.1:5190",
"ApiKey": "",
"TimeoutMilliseconds": 1000
}
}Refresh intervals must be between 5 and 3600 seconds; the stale threshold must be between 5 and 86400 seconds. ServiceCatalog:Services is supplied by services.json and must contain at least one service. Each service may identify its corresponding ContainerName and ProtocolServiceKey so live Agent data can be correlated with catalog metadata.
Dashboard:MobileApi:Enabled controls the bearer-authenticated Mobile API used
by installed Windows and Android clients. Dashboard:MobileApi:TokenHash is the
Base64-encoded SHA-256 hash of the expected token, not the raw token. Enter the
raw token independently in each installed app's Settings page; it is stored with
MAUI SecureStorage. Keep the Dashboard Archive:BaseUrl pointed at an
internal Archive URL.
GitActivityApi:BaseUrl must be an absolute HTTP or HTTPS URL.
GitActivityApi:ApiKey is required when the integration is enabled and must
match GitActivity:ApiKey on the private service. In container deployments,
configure these as GitActivityApi__BaseUrl, GitActivityApi__Enabled,
GitActivityApi__ApiKey, and GitActivity__ApiKey. Dashboard and Mobile do not
require public GitActivity exposure. If the API is separately exposed for a
trusted server-side integration, retain its API-key authentication and do not
ship that key to browser or Mobile clients.
One way to derive the placeholder hash locally with PowerShell is:
$token = Read-Host -AsSecureString "Mobile API token"
$ptr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($token)
try {
$plain = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($ptr)
[Convert]::ToBase64String(
[Security.Cryptography.SHA256]::HashData(
[Text.Encoding]::UTF8.GetBytes($plain)))
}
finally {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr)
}The optional Mission Control client publishes successful and failed Dashboard login events when enabled. Create a local Dashboard user from an interactive terminal with:
dotnet run --project MissionControl.Dashboard -- users create operator "Local Operator"GitActivity uses the shared RabbitMq and RabbitMqConsumer sections plus GitActivity.
{
"RabbitMq": {
"HostName": "localhost",
"Port": 5672,
"UserName": "mission-control",
"Password": "replace-with-a-secret",
"VirtualHost": "/mission-control",
"ClientProvidedName": "mission-control-git-activity-local"
},
"RabbitMqConsumer": {
"ExchangeName": "kgivler.events",
"QueueName": "mission-control.git-activity",
"RoutingKey": "github.push.received",
"PrefetchCount": 10
},
"GitActivity": {
"DatabaseFileName": "git-activity.db",
"BasePath": "Data",
"DefaultResultLimit": 10,
"MaxResultLimit": 50,
"ApiKey": "replace-with-at-least-32-characters",
"AllowedRepositories": [
"example-owner/example-repository"
],
"AllowedBranches": [
"main"
]
}
}The API key must contain at least 32 characters. Both allowlists must be non-empty, and the default result limit cannot exceed the maximum.
| Service | Endpoint | Notes |
|---|---|---|
| Gateway | POST /api/events |
Requires one configured X-Mission-Control-Key; accepts a generic integration event. |
| Gateway | POST /api/webhooks/github |
Requires GitHub event/delivery headers and a valid X-Hub-Signature-256 when enabled. |
| Archive | GET /api/events |
Recent complete archived events; supports limit/source/type/before filtering. |
| Archive | GET /api/events/feed |
Summary feed with a stable three-part cursor. |
| Archive | GET /api/events/{eventId} |
Complete event metadata and payload, or 404. |
| Archive | GET /api/events/statistics |
Archive totals, 24-hour counts, and top categories. |
| Dashboard Mobile API | GET /api/mobile/ping |
Requires Authorization: Bearer <raw-token> and validates the configured Dashboard:MobileApi:TokenHash. Used by installed clients to test Settings. |
| Dashboard Mobile API | GET /api/events/feed |
Requires the Mobile API bearer token. Proxies to the internal Archive client; supports limit/source/eventType and the three cursor fields. |
| Dashboard Mobile API | GET /api/events/statistics |
Requires the Mobile API bearer token. Proxies Archive statistics through Dashboard. |
| Dashboard Mobile API | GET /api/events/{eventId} |
Requires the Mobile API bearer token. Proxies complete Archive event details through Dashboard. |
| Dashboard Mobile API | GET /api/mobile/git-activity |
Requires the Mobile API bearer token. Returns a bounded recent feed through Dashboard's private GitActivity client with no-store headers. |
| Agent | GET /api/snapshot |
Latest sanitized node snapshot; returns 503 until one is stored. |
| GitActivity | GET /api/github/activity |
Recent allowed activity; requires X-Mission-Control-Key. |
Gateway, Archive, and GitActivity expose GET /health/live and GET /health/ready. Readiness includes their RabbitMQ status and, for SQLite consumers, database health. Agent exposes GET /health/live. Dashboard pages are cookie-authenticated and redirect anonymous users to /login.
Dashboard pages (/, /events, /events/{eventId}, /services,
/gitactivity, /login, and /logout) use normal cookie authentication. The Dashboard Mobile API uses
bearer authentication and does not use the Dashboard cookie.
Archive query endpoints and Agent liveness do not add application-level authentication. Place internal services behind appropriate network controls or an authenticated proxy when they are not intended to be public. The Mobile API exists so Archive can remain private while installed clients read Archive data through Dashboard.
Restore, build, and test the solution:
dotnet restore MissionControl.slnx
dotnet build MissionControl.slnx --configuration Debug
dotnet test MissionControl.slnx --configuration Debug --no-buildBuild the MAUI client on Windows for the checked-in Windows target:
.\scripts\Build-MissionControlWindows.ps1The script builds Release for win-x64 by default and does not publish,
launch, or install the app. Use -Configuration Debug for a development build,
-RuntimeIdentifier win-arm64 for Windows on ARM64, or -NoRestore when the
required assets have already been restored.
Build the MAUI client for Android:
dotnet build .\MissionControl.Mobile\MissionControl.Mobile.csproj `
-f net10.0-android `
-c DebugAfter supplying valid local configuration, run each executable in a separate terminal as needed:
dotnet run --project MissionControl.Gateway
dotnet run --project MissionControl.Archive -- --urls http://localhost:5191
dotnet run --project MissionControl.Agent
dotnet run --project MissionControl.Dashboard
dotnet run --project MissionControl.GitActivityChecked-in launch profiles use port 5190 for Gateway, 5194 for Agent, 5089/7062 for Dashboard, and 5242 for GitActivity. The explicit Archive command above matches the Dashboard’s checked-in Archive URL. Services that connect to RabbitMQ will not be fully operational until the broker and credentials are available.
MissionControl.Mobile currently uses:
ApplicationTitle:Mission ControlApplicationId:com.kgivler.missioncontrol.mobileApplicationDisplayVersion:1.0.2ApplicationVersion:3- Android Release package format:
apk - Windows package type:
None
For a private Windows install, publish an unpackaged, self-contained, win-x64 build:
dotnet publish .\MissionControl.Mobile\MissionControl.Mobile.csproj `
-f net10.0-windows10.0.19041.0 `
-c Release `
/p:RuntimeIdentifierOverride=win-x64 `
/p:SelfContained=true `
/p:WindowsPackageType=None `
/p:WindowsAppSDKSelfContained=trueCopy the entire publish directory to the target machine. The executable depends
on the other files in that directory; copying only the .exe is not enough.
The repository includes scripts\Update-MissionControlPhone.ps1 for installing
Mission Control on a new Android phone or updating an existing installation. It
requires an authorized ADB device and an existing signing keystore. The script
does not create the keystore.
Create the default keystore once, using the same password for the keystore and key when prompted:
New-Item -ItemType Directory -Force "$HOME\.missioncontrol"
keytool -genkeypair `
-keystore "$HOME\.missioncontrol\missioncontrol.keystore" `
-alias missioncontrol `
-keyalg RSA `
-keysize 2048 `
-validity 10000Back up the keystore and password securely. Android requires every future update to use the same signing key; losing it prevents updates to existing installations.
Enable USB debugging on the phone, connect and unlock it, approve the authorization prompt, then run this command from the repository root:
.\scripts\Update-MissionControlPhone.ps1The script increments the display and build versions, publishes a signed
Release APK, and runs adb install -r. That ADB command performs a first-time
install when the package is absent and preserves app data and SecureStorage
when updating an installation signed with the same key. The project version is
retained only after a successful installation.
If ADB is installed in the current Android SDK user directory, specify it explicitly:
.\scripts\Update-MissionControlPhone.ps1 `
-AdbPath "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe"Use -Version 1.1.0 to choose the next display version, or -DeviceSerial to
select a phone when multiple ADB devices are connected. -SkipVersionBump
rebuilds the current project version and may be rejected by Android if the
installed build number is newer.
For private Android sideloading, publish a signed Release APK. Keep the keystore file outside the repository and provide passwords through environment variables:
$env:MC_ANDROID_KEYSTORE = "C:\path\to\mission-control-upload.keystore"
$env:MC_ANDROID_KEY_ALIAS = "mission-control-upload"
$env:MC_ANDROID_STORE_PASS = "<store-password-from-secret-manager>"
$env:MC_ANDROID_KEY_PASS = "<key-password-from-secret-manager>"
dotnet publish .\MissionControl.Mobile\MissionControl.Mobile.csproj `
-f net10.0-android `
-c Release `
/p:AndroidKeyStore=true `
/p:AndroidSigningKeyStore="$env:MC_ANDROID_KEYSTORE" `
/p:AndroidSigningKeyAlias="$env:MC_ANDROID_KEY_ALIAS" `
/p:AndroidSigningStorePass="$env:MC_ANDROID_STORE_PASS" `
/p:AndroidSigningKeyPass="$env:MC_ANDROID_KEY_PASS"Install or update the APK with:
adb install -r .\path\to\com.kgivler.missioncontrol.mobile-Signed.apkRetain the same keystore and alias for future updates. Never commit the keystore, signing passwords, or release credentials. Google Play/AAB publishing is not the current release path documented here.
For releases, ApplicationDisplayVersion is the user-facing version,
ApplicationVersion must increase, and ApplicationId must remain stable.
Android updates must be signed with the same signing key.
The repository contains Dockerfiles for Gateway, Archive, Dashboard, and GitActivity:
docker build -f Dockerfile.gateway -t mission-control-gateway .
docker build -f Dockerfile.archive -t mission-control-archive .
docker build -f Dockerfile.dashboard -t mission-control-dashboard .
docker build -f Dockerfile.gitactivity -t mission-control-gitactivity .There is no Compose file checked into this repository and no Agent Dockerfile.
Production Compose orchestration is maintained externally in
UsefulScripts/VPS/docker-compose.yaml
and may trail application changes.
- Archive sets
EventArchive__BasePath=/app/dataand declares/app/dataas a volume; mount persistent storage there. - Dashboard declares
/app/datafor its authentication database and Data Protection keys. - Dashboard requires the private
GitActivityApi__BaseUrland matchingGitActivityApi__ApiKey; GitActivity requires the same value throughGitActivity__ApiKey. Supply that value through deployment secrets. - Gateway requires RabbitMQ, event-source, and optional webhook secrets through external configuration.
- GitActivity requires RabbitMQ, API-key, allowlist, and SQLite path configuration. Its Dockerfile does not declare a data volume, so persistent deployment storage must be arranged by the operator.
- An externally containerized Agent would require access to the configured Docker Unix socket, but this repository does not provide or endorse a Compose mounting recipe. Docker socket access is highly privileged.
The xUnit suite covers:
- generic Gateway authentication, request validation, and cancellation;
- GitHub signature, payload, owner, and normalization behavior;
- production Gateway publisher/health DI registration and RabbitMQ option validation;
- Gateway-to-Archive serialization, SQLite storage, querying, and deduplication;
- Archive section, filename, path, environment-variable, startup, and health validation;
- Agent host collection, Docker state/resource parsing, and collector-failure isolation;
- protocol probe execution, timeout, and public diagnostic sanitization;
- snapshot persistence, API/Dashboard contract compatibility, publication gating, retries, and metadata;
- Dashboard refresh, freshness, last-known-data, polling cancellation, paging, and new-event handling;
- GitActivity contracts, private-client authentication, Mobile proxy security, shared feed behavior, filtering, navigation, and storage projection behavior.
Run the repository formatting check with:
dotnet format MissionControl.slnx --verify-no-changes- Keep event-source keys, GitActivity keys, RabbitMQ credentials, GitHub webhook secrets, and Dashboard user credentials out of source control.
- Webhook signatures and API keys are validated before event publication; GitActivity compares its API key in fixed time.
- Keep Mobile API raw tokens, token hashes, Android keystores, signing passwords, and production host secrets out of source control.
- The Dashboard stores only the configured Mobile API token hash. Installed clients store the raw token independently in MAUI
SecureStorage. - Mobile Archive traffic should follow the Dashboard Mobile API proxy path; do not expose Archive publicly merely to support Windows or Android clients.
- Mobile GitActivity traffic must follow the Dashboard Mobile API proxy. MAUI
must never receive
X-Mission-Control-Key, the private GitActivity URL, or the GitActivity API key. - Dashboard authentication state depends on persistent SQLite and Data Protection key storage. Back up and permission those paths appropriately.
- Access to the Docker socket is effectively privileged host access. Grant it only to a trusted Agent process.
- Agent protocol endpoints and errors are sanitized before public serialization; local collector logs can contain more operational context and should be protected accordingly.
- SQLite paths are deployment state. Mount or back up Archive, Agent, Dashboard, and GitActivity storage according to the required retention.
- Agent storage retains only the latest snapshot per node; it is not a historical metrics database.
- Linux
/procsupplies the implemented host CPU, load-average, and memory metrics, and Docker collection currently targets a Unix socket. - Host uptime is not collected by the Agent.
- The repository does not include its externally maintained Compose orchestration or an Agent container image.
- Automated tests avoid external infrastructure; a real Gateway → RabbitMQ → Archive/GitActivity smoke test is still recommended for deployment validation.