Quickstart: Local Development with Aspire
Get Marginalia running locally using .NET Aspire. Aspire orchestrates all services (API, frontend, Azure AI Foundry) with a single command — including dependency installation, builds, and service startup.
Looking to deploy to Azure? See Deploy to Azure with Azure Developer CLI.
Prerequisites
.NET SDK 10.0
Install the .NET 10 SDK or later.
dotnet --version
# Expected: 10.0.x or laterAspire CLI
Install the Aspire CLI as a .NET global tool:
dotnet tool install --global Microsoft.Aspire.Cli --prereleaseVerify the installation:
aspire --versionNode.js and pnpm
Install Node.js 22 LTS or later, then enable pnpm:
corepack enable
corepack prepare pnpm@latest --activate
pnpm --versionAzure CLI
Install the Azure CLI and sign in:
az loginAzure subscription
You need an active Azure subscription with access to deploy Azure AI Foundry resources. The default region is swedencentral — ensure your subscription has quota for gpt-5.3-chat (GlobalStandard SKU) in that region.
1. Clone the repository
git clone https://github.com/PlagueHO/marginalia.git
cd marginalia2. Set required Azure user secrets for local provisioning
Aspire provisions Azure AI Foundry resources automatically on first run. Authenticate and provide subscription settings via dotnet user secrets so values stay out of source control.
# Sign in to Azure CLI for the target tenant
az login --tenant <tenant-id>
# Verify the correct subscription
az account show --query "{name:name, id:id, tenantId:tenantId}" -o tableSet required AppHost user secrets:
cd marginalia-service/src/Orchestration/AppHost
dotnet user-secrets set "Azure:SubscriptionId" "<subscription-id>"
dotnet user-secrets set "Azure:TenantId" "<tenant-id>"
dotnet user-secrets set "Azure:Location" "<azure-region>"Tip: Get your subscription and tenant IDs with
az account show. Useswedencentralto match the default local configuration, or set another supported Azure region if you update the AppHost settings accordingly.
Ensure all three keys are set:
Azure:SubscriptionIdAzure:TenantIdAzure:Location
3. Run the app
From the repository root, start Aspire:
cd marginalia-service/src/Orchestration/AppHost
aspire runAspire will:
- Install frontend dependencies (
pnpm install) - Build the .NET API project
- Provision Azure Microsoft Foundry resource with Foundry Project.
- Deploy the
reviewer(gpt-5.3-chat) model to the provisioned Foundry Project. - Start the API and frontend dev server.
- Open the Aspire Dashboard
First run takes several minutes while Azure AI Foundry resources are provisioned. Subsequent runs are much faster.
Default ports
| Service | URL |
|---|---|
| Aspire Dashboard | https://localhost:17280 |
| API (HTTP) | http://localhost:5279 |
| API (HTTPS) | https://localhost:7022 |
| Frontend (Vite) | http://localhost:5173 |
4. First usage walkthrough
- Open the Aspire Dashboard — the URL is shown in the terminal output when Aspire starts (typically
https://localhost:17280). - Find the frontend URL — in the dashboard, locate the
frontendresource and click its endpoint link (default:http://localhost:5173). - Upload a document — upload a
.docxfile or paste text directly into the editor. - Configure AI model settings — if you're not using the Aspire-provisioned model, update your endpoint and API key in the UI.
- Click "Analyze" — Marginalia sends your text to the AI model and returns suggestions for improving your narrative flow.
Model configuration
Aspire provisions this AI model deployment by default:
| Deployment | Model | Version | SKU | Capacity |
|---|---|---|---|---|
reviewer | gpt-5.3-chat | 2026-03-03 | GlobalStandard | 50 |
The dedicated judge deployment used by the AI evaluation pipeline is provisioned by the shared Azure infrastructure described in quickstart-azure.md, not by the local Aspire AppHost.
Override model settings
You can change the model name and version via environment variables:
| Setting | Environment variable | Default |
|---|---|---|
| Model name | MicrosoftFoundry__modelName | gpt-5.3-chat |
| Model version | MicrosoftFoundry__modelVersion | 2026-03-03 |
Set overrides with environment variables:
export MicrosoftFoundry__modelName="gpt-5.3-chat"
export MicrosoftFoundry__modelVersion="2026-03-03"Access code protection
When running in single-user mode (without Entra ID authentication), you can optionally protect the application with an access code. When configured, the frontend displays an access code dialog on first load and the backend API rejects requests without a valid X-Access-Code header.
When the access code is not set or empty, the application is fully open with no access gate — this is the default behavior.
Configure via appsettings
In marginalia-service/src/Api/appsettings.json (or appsettings.Development.json):
{
"AccessControl": {
"AccessCode": "your-access-code-here"
}
}Configure via environment variable
export ACCESS_CODE="your-access-code-here"Or on Windows:
$env:ACCESS_CODE = "your-access-code-here"Configure for production deployment
When deploying to Azure via GitHub Actions, add an ACCESS_CODE secret to your GitHub repository (or the prod environment). If the secret is set, the CD pipeline passes it through to the Azure Container App as an environment variable. If the secret is not set, the application runs without access code protection.
- Go to your GitHub repository Settings > Secrets and variables > Actions.
- Click New repository secret (or add to the
prodenvironment). - Set Name to
ACCESS_CODEand Value to your desired access code.
The secret flows through the workflow chain: continuous-delivery.yml → deploy-production.yml → provision-infrastructure.yml → Bicep accessCode parameter → Container App ACCESS_CODE environment variable.
How it works
- The frontend checks
GET /api/config/access-statuson startup to determine if an access code is required. - If required, a modal dialog prompts for the access code before allowing access.
- The access code is stored in the browser's session storage (cleared when the tab closes).
- All API requests include the access code in the
X-Access-Codeheader. - Health endpoints (
/health,/alive) and the access status endpoint are not protected.
Note: This is a lightweight access gate for single-user deployments, not a high-security authentication mechanism. For production multi-user deployments, use Entra ID authentication.
Run tests
Backend
cd marginalia-service
dotnet test Marginalia.slnxFrontend
cd marginalia-app
pnpm testTroubleshooting
| Symptom | Fix |
|---|---|
aspire command not found | Install with dotnet tool install --global Microsoft.Aspire.Cli --prerelease |
| Azure provisioning fails with quota error | Ensure your subscription has gpt-5.3-chat GlobalStandard quota in swedencentral. Try a different region by setting Azure__Location in launchSettings. |
pnpm: command not found | Run corepack enable && corepack prepare pnpm@latest --activate |
| Frontend shows connection errors | Ensure the API is running — check the Aspire Dashboard for service health. The frontend waits for the API via WaitFor. |
| Port conflicts | Another process is using the default ports. Stop conflicting processes or update launchSettings.json in the AppHost and API projects. |
Azure:SubscriptionId not set | Set user secrets as described in step 2. Aspire needs these to provision AI Foundry resources. |
Next steps
- Deploy to Azure — see Deploy to Azure with Azure Developer CLI (coming soon).
- Architecture — read the PRD for product requirements and design context.