Installation
Beacon ships two ways. Pick the path that fits you:
- Path A — Run the self-hostable application: clone the repo and run
Beacon.SampleProject. Fastest way to evaluate Beacon and the recommended path for contributors. - Path B — Embed Beacon as NuGet packages: reference the
Beacon.*packages and wire them into your own ASP.NET Core app.
Both paths use the same configuration model (see the Configuration Guide).
Prerequisites
Section titled “Prerequisites”Before you begin, ensure you have:
- .NET 10.0 SDK or later
- PostgreSQL 12+ or SQL Server 2019+ for Beacon’s metadata database
- On PostgreSQL, the pgvector extension available on the server — a migration runs
CREATE EXTENSION IF NOT EXISTS vector, so startup migrations fail without it. On Azure Database for PostgreSQL Flexible Server, allow-list it first (server parameterazure.extensionsmust includeVECTOR), or have an admin runCREATE EXTENSION vector;once if Beacon’s database role cannot create extensions - Node.js 18+ and npm — only needed to build or run the React frontend from source
- Visual Studio 2022, Rider, or VS Code with C# support
- An encryption key (
Beacon:EncryptionKey) — required (see Step 2)
Path A — Run the Self-Hostable Application
Section titled “Path A — Run the Self-Hostable Application”The Beacon.SampleProject host is the composition root. It self-hosts Kestrel, serves the React SPA at the root URL /, exposes the REST API / MCP server / SignalR hub, and includes a working scheduler implementation.
A1. Clone the repository
Section titled “A1. Clone the repository”git clone https://github.com/moberghr/beacon.gitcd beaconA2. Configure secrets
Section titled “A2. Configure secrets”Set the metadata connection string and the required encryption key. Use User Secrets (recommended) or appsettings.Development.json:
dotnet user-secrets --project Beacon.SampleProject set \ "ConnectionStrings:BeaconContext" \ "Host=localhost;Database=beacon;Username=postgres;Password=yourpassword"
dotnet user-secrets --project Beacon.SampleProject set \ "Beacon:EncryptionKey" "$(openssl rand -base64 32)"A3. Run the API host
Section titled “A3. Run the API host”dotnet run --project Beacon.SampleProject --no-launch-profileThis starts Kestrel on:
- HTTP: http://localhost:5296
- HTTPS: https://localhost:7187
On first run Beacon automatically applies EF Core migrations and creates the beacon schema. Verify it’s up with the health check:
curl http://localhost:5296/beacon/api/healthA4. (Optional) Run the React dev server
Section titled “A4. (Optional) Run the React dev server”The host serves the pre-built SPA out of src/Beacon.UI/wwwroot. If you’re working on the frontend, run the Vite dev server instead — it hot-reloads and proxies API/MCP calls to Kestrel:
npm install --prefix src/Beacon.UI/webnpm run dev --prefix src/Beacon.UI/webVite serves the app on http://localhost:5173 and proxies /beacon/api and /beacon/mcp to Kestrel (port 5296 / 7187), so keep the API host from A3 running alongside it.
Other frontend commands (run inside src/Beacon.UI/web):
| Command | What it does |
|---|---|
npm run build | Production build → outputs to src/Beacon.UI/wwwroot |
npm run codegen | Regenerates the typed TS fetch client from /openapi/v1.json via NSwag |
npm test | Runs the Vitest test suite |
A5. Open the app
Section titled “A5. Open the app”| URL | What |
|---|---|
/ (e.g. https://localhost:7187/) | Beacon React SPA |
/login | Login form |
/beacon/api/health | API health check |
/openapi/v1.json | OpenAPI document |
/beacon/mcp | MCP server (auth required) |
On the first run Beacon walks you through a setup flow that creates the initial admin user. There are no hardcoded credentials — you set them during setup. The setup page asks for a setup token: the value of Beacon:UserManagement:SetupToken, or, when it is not set, the token Beacon prints once to the process console (standard error, not the log) at startup while no super admin has ever existed. A configured token must be at least 32 characters. Configure the token explicitly when several replicas serve the setup page, since each replica generates its own.
Path B — Embed Beacon as NuGet Packages
Section titled “Path B — Embed Beacon as NuGet Packages”Embed Beacon into your own ASP.NET Core app by referencing the Moberg.Beacon.* packages.
B1. Install NuGet packages
Section titled “B1. Install NuGet packages”# Core + metadata DB provider (choose one provider)dotnet add package Moberg.Beacon.Coredotnet add package Moberg.Beacon.Core.PostgreSql # or: Moberg.Beacon.Core.SqlServer
# UI (React SPA shipped as a Razor Class Library), REST API, AI, MCPdotnet add package Moberg.Beacon.UIdotnet add package Moberg.Beacon.Apidotnet add package Moberg.Beacon.AIdotnet add package Moberg.Beacon.MCP
# Data-source connectors — add only the ones you needdotnet add package Moberg.Beacon.Connector.PostgreSqldotnet add package Moberg.Beacon.Connector.SqlServerdotnet add package Moberg.Beacon.Connector.MySqldotnet add package Moberg.Beacon.Connector.BigQuerydotnet add package Moberg.Beacon.Connector.Snowflakedotnet add package Moberg.Beacon.Connector.Databricksdotnet add package Moberg.Beacon.Connector.AzureSynapsedotnet add package Moberg.Beacon.Connector.CloudWatchdotnet add package Moberg.Beacon.Connector.ApiYou will also need a job runner for scheduled work — see B5.
B2. Generate the encryption key
Section titled “B2. Generate the encryption key”See Step 2 below.
B3. Configure appsettings.json
Section titled “B3. Configure appsettings.json”Keep Beacon:EncryptionKey out of this file: supply it through User Secrets, the Beacon__EncryptionKey environment variable or a secrets manager (see Step 2).
{ "ConnectionStrings": { "BeaconContext": "Host=localhost;Database=beacon;Username=postgres;Password=yourpassword" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning", "Microsoft.EntityFrameworkCore.Database": "Warning" } }}See the Configuration Guide for AI/LLM, OIDC, email, and scheduling options.
B4. Wire up Program.cs
Section titled “B4. Wire up Program.cs”This is the full host setup, modeled on src/Beacon.SampleProject/Program.cs:
using Beacon.AI;using Beacon.Api;using Beacon.Api.Endpoints; // MapBeaconApi / MapLoginEndpointsusing Beacon.Api.Hubs; // BeaconHubusing Beacon.Core;using Beacon.Core.PostgreSql;using Beacon.MCP;using Beacon.UI;
var builder = WebApplication.CreateBuilder(args);
// 1. Your job runner (see B5) — register it here, e.g. Moberg Warp's AddWarpWorker(...)
// 2. Host identity (claims transformation)builder.Services.AddBeaconHostInfrastructure<YourClaimsTransformation>(); // your IClaimsTransformation
// 3. Core services, scheduler, connectors, metadata providerbuilder.Services.AddBeaconServices(builder.Configuration, options => { options.AddBeaconScheduler<BeaconScheduler>(); // your IBeaconScheduler (see B5) options.BaseUrl = "https://localhost:7187"; options.UseAI = true; options.AddEmailAdapter<BeaconMailSender>(); // your IEmailAdapter impl
// Auth + user management options.Authorization.Enabled = true; options.Authentication.EnableLoginForm = true; options.AddAuthenticationProvider<DatabaseAuthenticationProvider>(); options.UserManagement = new UserManagementOptions { Enabled = true }; }) .AddPostgreSqlConnector() .AddSqlServerConnector() .AddMySqlConnector() .AddCloudWatchConnector() .AddAzureSynapseConnector() .AddSnowflakeConnector() .AddDatabricksConnector() .AddBigQueryConnector() .AddApiConnector() .UsePostgreSql(builder.Configuration.GetConnectionString("BeaconContext")!, "beacon");
// 4. REST API + real-time. SignalR is wired for you — add nothing.// Opt out with: AddBeaconApiServices(x => x.Realtime = false)builder.Services.AddBeaconApiServices();builder.Services.AddBeaconApiAuthorization(); // the API, Admin and Execute-scope policies (MapBeaconApi, MapBeaconMcp)
// 5. Authentication, AI, MCP, OpenAPIbuilder.Services.AddBeaconCookieAuthentication("/"); // login redirect targetbuilder.Services.AddBeaconOidcAuthentication(builder.Configuration); // optional SSObuilder.Services.AddBeaconAI(builder.Configuration);builder.Services.AddBeaconMcp();builder.Services.AddOpenApi();
var app = builder.Build();
// 6. Middleware order is load-bearingapp.UseStaticFiles();app.UseMiddleware<ApiKeyAuthMiddleware>();app.UseAuthentication();app.UseMiddleware<BeaconCookieAuthMiddleware>();app.UseAuthorization();app.UseAntiforgery();
// 7. Endpointsapp.MapOpenApi(); // /openapi/v1.jsonapp.MapBeaconApi(); // /beacon/api/* + the SignalR hub at /beacon/api/hubapp.MapLoginEndpoints("/beacon", beaconConfiguration);app.MapBeaconMcp(); // MCP server at /beacon/mcp, behind the Execute-scope policyapp.MapBeaconUi(); // React SPA at root /
app.Run();Real-time (SignalR)
Section titled “Real-time (SignalR)”Events pushed to the hub: JobStatusChanged, NotificationCreated, ApprovalUpdated — each scoped
to the individual user via Clients.User(...).
Turning it off
Section titled “Turning it off”If your host has no use for real-time updates:
builder.Services.AddBeaconApiServices(x => x.Realtime = false);What changes:
| Realtime on (default) | Realtime off | |
|---|---|---|
| SignalR services | registered | not registered |
/beacon/api/hub | mapped | not mapped |
IApprovalNotifier | SignalRApprovalNotifier | RealtimeDisabledApprovalNotifier (a deliberate no-op) |
| React shell | connects to the hub | never attempts a connection |
The shell reads realtimeEnabled from /beacon/api/auth/me, so it knows realtime is off rather than
discovering it by failing — it will not poll a route that isn’t there.
Configuring SignalR
Section titled “Configuring SignalR”For a custom protocol or a backplane, reach the ISignalRServerBuilder:
builder.Services.AddBeaconApiServices(x => x.ConfigureSignalR = signalr => signalr.AddStackExchangeRedis(redisConnectionString));Calling AddSignalR() yourself as well is safe — registration is additive.
PostgreSQL through your host’s NpgsqlDataSource
Section titled “PostgreSQL through your host’s NpgsqlDataSource”If your host already owns an NpgsqlDataSource — for example one Aspire builds with passwordless Microsoft Entra
authentication, whose connection string has no password — hand it to Beacon instead of a connection string.
Beacon then opens every connection (EF Core, the startup migrations in UseBeacon(), raw SQL) from it:
// Aspire registers NpgsqlDataSource as a singleton (Aspire.Azure.Npgsql / Aspire.Npgsql)builder.AddAzureNpgsqlDataSource("beacondb");
builder.Services.AddBeaconServices(builder.Configuration, options => { /* ... */ }) // ... connectors ... .UsePostgreSql(sp => sp.GetRequiredService<NpgsqlDataSource>());
// A keyed registration (AddKeyedAzureNpgsqlDataSource) works the same way:// .UsePostgreSql(sp => sp.GetRequiredKeyedService<NpgsqlDataSource>("beacondb"));// Or pass an instance you built yourself: .UsePostgreSql(dataSource)The factory is called once, from the root provider, when Beacon first builds its context options.
ConnectionStrings:BeaconContext is not read on this path.
What Beacon needs from the data source:
- No
UseVector(). Beacon never sends or reads the pgvector type through Npgsql — it casts text literals tovector(384)in SQL — so the data source needs no vector type mapping. The database needs thevectorextension: Beacon’s migration runsCREATE EXTENSION IF NOT EXISTS vector, which requiresVECTORinazure.extensionson Azure Flexible Server and a role allowed to create it (or an admin runningCREATE EXTENSION vector;beforehand). - Its own search path stays yours. Beacon’s SQL names its tables and the vector type unqualified, so on each
connection it opens Beacon runs
SET search_path TO "beacon", public. Npgsql resets session state when a connection returns to the pool, so your own code never sees it — keep that reset on (do not setNo Reset On Close=true) and do not enableMultiplexingon a data source you share with Beacon. If you cannot meet that, register a separate keyed data source for Beacon. - Rights in the
beaconschema. The data source’s role must be able to create thebeaconschema (or own it) and create tables in it;ServiceConfiguration.UseBeacon(app.Services, createSchema: true)creates the schema if missing.
For SQL Server
Section titled “For SQL Server”If you use SQL Server for Beacon’s metadata instead of PostgreSQL:
using Beacon.Core.SqlServer;
// Metadata providerbuilder.Services.AddBeaconServices(builder.Configuration, options => { options.AddBeaconScheduler<BeaconScheduler>(); }) // ... connectors ... .UseSqlServer(builder.Configuration.GetConnectionString("BeaconContext")!, "beacon");Update the connection string in appsettings.json:
{ "ConnectionStrings": { "BeaconContext": "Server=localhost;Database=beacon;User Id=sa;Password=YourPassword123!;TrustServerCertificate=True" }}B5. Provide a scheduler implementation
Section titled “B5. Provide a scheduler implementation”Beacon does not bundle a job runner — it schedules work through the IBeaconScheduler abstraction, so it plugs into whatever your host already uses. Beacon calls AddOrUpdate when a subscription is created or its cron changes, and Remove when it’s deleted or disabled; your implementation maps those calls onto recurring jobs that invoke IJobService.ExecuteQuery(subscriptionId).
No Beacon package references a job runner. All the background work itself already ships with the
packages you installed — IJobService (MCP eval, learned-pattern aggregation, signal cleanup,
embedding reindex, subscription execution) in Moberg.Beacon.Core, and documentation generation
plus AI actor think-cycles in Moberg.Beacon.AI. What you supply is a thin adapter per job that
forwards to those services on your runner’s terms. src/Beacon.SampleProject/Warp/Jobs/ is the
reference implementation of that pattern, written against Moberg Warp.
using Beacon.Core.Worker;
namespace YourProject.Services;
public class BeaconScheduler : IBeaconScheduler{ public void AddOrUpdate(int subscriptionId, string subscriptionName, string cron) { var jobKey = $"{subscriptionId} - {subscriptionName}"; // register/update a recurring job in your job runner that calls // IJobService.ExecuteQuery(subscriptionId) on the given cron schedule }
public void Remove(int subscriptionId, string subscriptionName) { var jobKey = $"{subscriptionId} - {subscriptionName}"; // remove the recurring job }}Any job runner with cron/recurring support works. We recommend Moberg Warp: define an IJob that calls IJobService.ExecuteQuery, and map AddOrUpdate/Remove onto Warp’s recurring-job APIs — you get retries, concurrency guards ([Mutex]), and a job dashboard out of the box. Quartz.NET is another valid choice, and Beacon.SampleProject ships a complete working reference implementation you can copy as a starting point.
B6. (Optional) Extended timeouts for AI operations
Section titled “B6. (Optional) Extended timeouts for AI operations”AI calls can run for minutes. Kestrel keep-alive and request-header timeouts and the default HttpClient timeout are tuned to 5 minutes:
builder.WebHost.ConfigureKestrel(serverOptions =>{ serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(5); serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(5);});
builder.Services.AddHttpClient().ConfigureHttpClientDefaults(http =>{ http.ConfigureHttpClient(client => { client.Timeout = TimeSpan.FromMinutes(5); });});B7. Run
Section titled “B7. Run”dotnet runOn first run Beacon applies EF Core migrations, creates the beacon schema, and walks you through the first-run setup flow that creates the initial admin user. Then open:
| URL | What |
|---|---|
/ | Beacon React SPA |
/login | Login form |
Step 2: Generate the Encryption Key
Section titled “Step 2: Generate the Encryption Key”Beacon requires an encryption key (Beacon:EncryptionKey) to encrypt sensitive data — most importantly data-source connection strings — at rest with AES-256. It must be base64 that decodes to at least 32 random bytes; generate one with:
openssl rand -base64 32Store it via User Secrets, the Beacon__EncryptionKey environment variable (double underscore), or a secrets manager — never commit it, and never copy a key printed in a guide. Beacon refuses at startup, in every environment, a missing or short key, a passphrase, a published sample value and a ${...} placeholder (.NET configuration does not expand it). See the Configuration Guide for production patterns and for replacing a key.
Authentication
Section titled “Authentication”Beacon authentication is cookie-based (the Beacon.Auth cookie is HttpOnly, SameSite=Lax) and driven by a pluggable IBeaconAuthenticationProvider. The sample uses DatabaseAuthenticationProvider (internal users with passwords stored in Beacon). There is no basic auth and no admin/admin default — the first-run setup flow creates the initial admin user.
Beacon supports:
- Login form — React
/loginroute, backed by the cookie scheme - OIDC / SSO — optional, via
AddBeaconOidcAuthentication(...) - JWT bearer — for MCP clients
- API keys — SHA256-hashed at rest, carry scopes (
Read,Execute) and optional project restrictions, always expire, and stop working when their owner is disabled; the raw key is shown once at creation
See the Configuration Guide and the User Management Guide for details.
Troubleshooting
Section titled “Troubleshooting””Beacon:EncryptionKey must be configured” (or “must be base64…”, “is a publicly known value”)
Section titled “”Beacon:EncryptionKey must be configured” (or “must be base64…”, “is a publicly known value”)”Generate and configure a new key:
openssl rand -base64 32If values were already encrypted with the key that was refused, list that key in Beacon:PreviousEncryptionKeys so they stay readable (see Configuration → Replacing the key).
”Cannot create database schema”
Section titled “”Cannot create database schema””Ensure the Beacon database user has permission to create a schema.
PostgreSQL:
GRANT CREATE ON DATABASE beacon TO your_user;SQL Server:
GRANT CREATE SCHEMA TO your_user;Jobs not executing
Section titled “Jobs not executing”- Verify your job runner is registered and its worker is running.
- Check your scheduler’s dashboard or logs for job status.
- Confirm the database or storage your job runner uses is reachable.
- Confirm your
IBeaconSchedulerimplementation is registered viaoptions.AddBeaconScheduler<...>().
SPA not loading at /
Section titled “SPA not loading at /”- Confirm
app.MapBeaconUi()is wired andapp.UseStaticFiles()runs before it. - If developing the frontend, make sure the Vite dev server (
npm run dev) is running, or that you rannpm run buildsosrc/Beacon.UI/wwwrootis up to date. - Check the browser console for errors and clear cache.
Authentication failing
Section titled “Authentication failing”- Confirm
AddBeaconCookieAuthentication("/")is registered andUseAuthenticationruns in the correct order. - Verify the authentication provider (e.g.
DatabaseAuthenticationProvider) is registered. - For API-key callers, confirm the key’s scope and project restriction allow the request.
AI features not working
Section titled “AI features not working”- Verify
options.UseAI = true. - Check the LLM configuration (provider, key) in
appsettings.jsonor Admin Settings. - Confirm the provider key is valid and within quota.
- Ensure the extended timeouts (B6) are configured.
Production Considerations
Section titled “Production Considerations”Security
Section titled “Security”- Set a strong admin password during first-run setup — there are no default credentials. Keep the setup token out of shared channels; it stops working once the super admin exists.
- Keep the encryption key out of source control — use environment variables or a secrets manager.
- Enable HTTPS in production.
- Use OIDC/SSO for production identity where possible.
- Scope API keys tightly (
Read/Execute/Admin+ project restrictions).
Performance
Section titled “Performance”- Configure database connection pooling.
- Adjust your job runner’s worker count to match subscription load.
- Set query timeouts appropriate to your workloads.
Monitoring
Section titled “Monitoring”- Enable detailed logging for troubleshooting (no PII in logs).
- Use your job runner’s dashboard to monitor job execution.
- Track LLM token usage and cost if AI is enabled.
Next Steps
Section titled “Next Steps”Now that Beacon is running:
- Connect your first data source — add databases and APIs to monitor
- Create your first query — define SQL monitoring queries (including cross-database)
- Set up a subscription — schedule automated execution
- Configure notifications — deliver results via Email, Teams, or Jira
Additional Resources
Section titled “Additional Resources”- Configuration Guide — detailed configuration options
- Quick Start — create your first alert end to end
- Features Overview — complete feature documentation
- Report Issues