Skip to content

Instantly share code, notes, and snippets.

@sliekens
Last active June 10, 2026 21:25
Show Gist options
  • Select an option

  • Save sliekens/fee168922e3b63de06b5d1c899050e77 to your computer and use it in GitHub Desktop.

Select an option

Save sliekens/fee168922e3b63de06b5d1c899050e77 to your computer and use it in GitHub Desktop.
Aspire GlitchTip planning docs

GlitchTip Deploy-Mode Provisioning — Scenario Analysis

Problem statement

aspire deploy must get a GlitchTip DSN — which only exists after the GlitchTip server is running and an org/team/project has been created — into the configuration of consuming app services.

Hard requirements:

  1. Compute-environment agnostic. The design must work for Docker Compose, Kubernetes, and future compute environments. It must not depend on publisher-specific artifacts (patching .env files, rewriting Helm values, re-running docker compose up).
  2. The deploy (CI) machine has HTTP access to the published GlitchTip endpoint (no firewall restrictions).
  3. Run mode keeps the existing in-process provisioning (ResourceReadyEventGlitchTipProvisionerDsnKey/ProjectId), which works well with WaitFor(glitchtip).

The fundamental gap (verified against Aspire source)

There is no Aspire-native way to run a resource, provision it during deployment, and feed the resulting value into sibling resources' environment using only WaitFor:

  • Every publisher resolves all values at artifact-generation time: Docker Compose writes parameter values to .env.{env} in prepare-{env} (DockerComposeEnvironmentResource.PrepareAsync); Kubernetes resolves parameters into Helm values.yaml during its prepare step (KubernetesResource — "Parameter values resolved during the prepare step, replacing the placeholder in values.yaml").
  • The deploy pipeline never runs the app model, so ResourceReadyEvent never fires and ConnectionStringExpression is never lazily awaited the way it is in run mode.
  • WaitFor translates to startup ordering only (compose depends_on: service_started; the service_healthy condition is commented out in DockerComposeServiceResource.SetDependsOn). It cannot carry a value.
  • There is no post-deploy feedback loop: nothing reads state from the deployed application back into consumer configuration within the same pipeline run.

This is a general Aspire limitation, not a GlitchTip quirk — it affects any service requiring post-start bootstrap that yields credentials/identifiers (Keycloak client secrets, Grafana API tokens, MinIO access keys, Vault tokens). It is captured as an upstream feature request: see aspire-issue-draft.md.

The one compute-agnostic channel: parameters + deployment state

What is portable across publishers, verified end to end:

  1. A ParameterResource referenced from ConnectionStringExpression is materialized natively by every publisher (compose → ${GLITCHTIP_DSN_KEY} placeholder + env file; k8s → values.yaml entry/Secret).
  2. Deployment state is loaded into IConfiguration at AppHost startup (DistributedApplicationBuilder.LoadDeploymentStateAddJsonFile), so a value stored under Parameters:{name} resolves the parameter on the next pipeline run, regardless of compute environment.
  3. IDeploymentStateManager is writable from any pipeline step (AcquireSectionAsyncSetValueSaveSectionAsync).

The corollary: a value computed during deploy can only take effect at the next artifact generation. Single-deploy convergence is impossible today without publisher-specific hacks (env patching + re-up was the rejected compose-only variant of this design).

Options

# Option Compute-agnostic First-deploy UX Verdict
A Parameters only; manual provisioning. Publish-mode connection string is built from {name}-dsn-key / {name}-project-id parameters. The operator provisions GlitchTip once (UI or script), sets the two values in CI config (Parameters__glitchtip-dsn-key=...), redeploys Sentry disabled until values supplied v1 baseline — zero pipeline code, works everywhere
B A + post-deploy provisioning step. A step ordered after compute deploy polls the GlitchTip URL from CI, runs the existing HTTP provisioning flow, and saves the values to deployment state under Parameters:{name}-*. Values flow into artifacts on the next deploy automatically ✅ (step touches only HTTP + deployment state) Sentry disabled on first deploy; auto-converges on second v1 recommended add-on — strictly additive over A, severable if unwanted
C Patch publisher artifacts in-pipeline and re-trigger the deploy step (env-file rewrite + second ComposeUpAsync) ❌ compose-only Single-deploy convergence ❌ Rejected: violates requirement 1
D Provisioner job/sidecar container inside the target network ❌ per-target output plumbing ❌ Rejected earlier: image publishing burden, no portable output channel
E Wait for upstream Aspire support (deferred/pipeline-produced values) Single-deploy File the issue; adopt when it exists

Recommendation: A + B now, E later. If B proves troublesome, dropping it leaves A fully functional (per the explicit fallback decision: deploy-mode auto-provisioning is droppable; run mode keeps it).

Why option B stays agnostic

  • It needs an externally reachable URL — supplied by the {name}-provision-url parameter (required for k8s/remote docker; defaults to http://localhost:{port} when an explicit host port was configured, matching the local-compose deploy story).
  • Ordering "after compute is deployed" has no portable hook (compose's up step is tagged docker-compose-up; k8s uses helm-deploy-{env} names; no shared DeployCompute tag in practice). The step therefore: adds guarded DependsOn edges for known step names when present (docker-compose-up-{env}), and otherwise relies on its own readiness polling — it waits for GET /api/0/ to return 200 with a generous timeout, so even running concurrently with the deploy step it converges. Polling is required anyway (first boot runs Django migrations).
  • It writes only to deployment state. No artifact is touched; each publisher's own prepare step materializes the values next run.

First-deploy behavior (both A and B)

The DSN parameters use the AddParameter(name, Func<string>) overload reading Parameters:{name} from configuration with an empty-string fallback — they never prompt (headless CI safe) and resolve from deployment state once present. On the first deploy consumers receive an incomplete DSN (http://@glitchtip:8000/). The companion client package CommunityToolkit.Aspire.GlitchTip must map a key-less DSN to an empty Sentry:Dsn (SDK disabled) instead of passing it to SentrySdk.Init, which throws on malformed DSNs. Apps run normally; they just don't report errors until the DSN lands.

Constraint catalog (verified)

Constraint Source
Publishers resolve all values at artifact-generation time; no post-deploy feedback into config DockerComposeEnvironmentResource.PrepareAsync, KubernetesResource values.yaml handling
WaitFor → ordering only; no value channel; compose emits service_started (healthy commented out) DockerComposeServiceResource.SetDependsOn
Parameters materialize natively per publisher; param config key convention Parameters:{name} (ConfigurationKey is internal) DockerComposeServiceExtensions.AsEnvironmentPlaceholder, ParameterResource
Deployment state JSON loads into IConfiguration at startup → params round-trip across runs DistributedApplicationBuilder.LoadDeploymentState
Empty parameter values are not persisted by the parameter processor (no state poisoning) ParameterProcessor.SaveParametersToDeploymentStateAsync
No portable "after compute deploy" ordering hook (publisher-specific step names/tags) DockerComposeEnvironmentResource (docker-compose-up-{env}), KubernetesEnvironmentResource (helm-deploy-{env})
Pipeline APIs need #pragma warning disable ASPIREPIPELINES001 (state: 002) Pipeline API annotations

Open questions

  1. Endpoint exposure: provisioning over HTTP requires a published/reachable endpoint per target (compose host port, k8s ingress). Auto-mark external in publish mode, or document + fail fast?
  2. Should option B be opt-in (WithDeployTimeProvisioning()) rather than default, so users who provision out-of-band never pay the polling timeout on deploys where GlitchTip is unreachable from CI?
  3. DSN rotation: B self-heals (always re-runs the idempotent ensure flow and updates state on drift) — but rotated values again take one extra deploy to land. Acceptable?

GlitchTip deploy-mode provisioning — implementation plan

Goal

Deploy-mode support that is compute-environment agnostic: the GlitchTip connection string flows to consumers through standard Aspire parameters on every publisher (Docker Compose, Kubernetes, …), with no publisher-specific artifact manipulation. Provisioning values are supplied either manually (baseline) or by an optional post-deploy pipeline step that persists them to deployment state (auto-convergence on the next deploy).

Run mode is unchanged: ResourceReadyEvent → in-process provisioner → DsnKey/ProjectId on the resource, with WaitFor(glitchtip) guaranteeing availability.

See deploy-design.md for the analysis and aspire-issue-draft.md for the upstream feature request covering the underlying platform gap (single-deploy convergence is impossible today).


High-level design

 aspire deploy (any compute environment)
 ─────────────────────────────────────────────────────────────────────────────
 process-parameters       {name}-dsn-key / {name}-project-id resolve from
        │                 Parameters:{name}-* config (deployment state),
        │                 fall back to "" — never prompt on CI
        │
 publish / prepare        publisher materializes the parameters natively:
        │                   compose → ${GLITCHTIP_DSN_KEY} + .env.{env}
        │                   k8s     → values.yaml / Secret
        │
 <compute deploy step>    services start; first deploy: DSN incomplete →
        │                 client package disables Sentry (no crash)
        │
 provision-glitchtip-{name}   [optional step — option B]
        │                 ① poll {provision-url}/api/0/ until 200 (timeout ~5 min)
        │                 ② HTTP from CI: auth, ensure org/team/project, fetch DSN
        │                 ③ save Parameters:{name}-dsn-key / -project-id
        │                    to deployment state (IDeploymentStateManager)
        ▼
 deploy complete          next `aspire deploy`: state → config → parameters
                          resolve up front → consumers get the real DSN
 ─────────────────────────────────────────────────────────────────────────────

No artifact is ever patched; no deploy step is re-run. The step writes only to deployment state — the single compute-agnostic channel.


Changes to GlitchTipResource

Internal publish-mode parameter slots; connection string branches on their presence:

internal ParameterResource? DsnKeyParameter { get; set; }      // publish mode only
internal ParameterResource? ProjectIdParameter { get; set; }   // publish mode only

public ReferenceExpression ConnectionStringExpression =>
    DsnKeyParameter is not null && ProjectIdParameter is not null
        ? ReferenceExpression.Create(
            $"{PrimaryEndpoint.Property(EndpointProperty.Scheme)}://{DsnKeyParameter}@{PrimaryEndpoint.Property(EndpointProperty.Host)}:{PrimaryEndpoint.Property(EndpointProperty.Port)}/{ProjectIdParameter}")
        : /* run mode: existing string-based expression (DsnKey / ProjectId) */;

Each publisher resolves the endpoint parts and parameters with its own conventions — compose yields http://${GLITCHTIP_DSN_KEY}@glitchtip:8000/${GLITCHTIP_PROJECT_ID}, Kubernetes the equivalent via values.yaml. The toolkit never needs to know which.


Changes to GlitchTipProvisioner

Extract a transport-only core shared by run mode and the deploy step:

internal static async Task<(string DsnKey, string ProjectId)> ProvisionAsync(
    string baseUrl, string email, string password,
    string orgName, string projectName,
    ILogger logger, CancellationToken ct)

The run-mode entry point becomes a wrapper resolving endpoint/parameters and assigning the resource properties. GlitchTipAuthClient/GlitchTipApiClient unchanged.


Changes to AddGlitchTip (publish mode only)

if (builder.ExecutionContext.IsPublishMode)
{
    // Deployment state loads into configuration at startup, so Parameters:{name}-*
    // resolves automatically once provisioned. Empty fallback ⇒ never prompts.
    var dsnKeyParam = builder.AddParameter($"{name}-dsn-key",
        () => builder.Configuration[$"Parameters:{name}-dsn-key"] ?? "", secret: true).Resource;
    var projectIdParam = builder.AddParameter($"{name}-project-id",
        () => builder.Configuration[$"Parameters:{name}-project-id"] ?? "").Resource;
    resource.DsnKeyParameter = dsnKeyParam;
    resource.ProjectIdParameter = projectIdParam;
}

This alone delivers the manual baseline (option A): provision GlitchTip once by hand, set Parameters__glitchtip-dsn-key / Parameters__glitchtip-project-id in CI configuration, deploy.

Optional automatic provisioning (option B)

    var provisionUrlParam = builder.AddParameter($"{name}-provision-url",
        () => builder.Configuration[$"Parameters:{name}-provision-url"] ?? "").Resource;

    resourceBuilder.WithPipelineStepFactory(
        $"provision-glitchtip-{name}",
        ctx => GlitchTipDeployStep.ExecuteAsync(ctx, resource, provisionUrlParam),
        dependsOn: [WellKnownPipelineSteps.DeployPrereq],
        requiredBy: [WellKnownPipelineSteps.Deploy]);

    // Best-effort ordering after known compute deploy steps; the step's readiness
    // polling makes ordering a nicety, not a correctness requirement.
    resourceBuilder.WithPipelineConfiguration(ctx =>
    {
        var step = ctx.Steps.FirstOrDefault(s => s.Name == $"provision-glitchtip-{name}");
        if (step is null) return;
        foreach (var env in ctx.Model.Resources.OfType<IComputeEnvironmentResource>())
        {
            foreach (var candidate in new[] { $"docker-compose-up-{env.Name}", $"helm-deploy-{env.Name}" })
            {
                if (ctx.Steps.Any(s => s.Name == candidate))
                    step.DependsOn(candidate);   // guarded: unknown names throw
            }
        }
    });

Decision pending (open question 2 in the design doc): always-on vs. opt-in via a WithDeployTimeProvisioning() extension. Plan assumes opt-in to keep option A users free of the polling timeout.

Pragmas: ASPIREPIPELINES001 for all pipeline APIs, ASPIREPIPELINES002 for IDeploymentStateManager.


New file: GlitchTipDeployStep.cs

  1. Resolve inputs via GetValueAsync: admin email/password, org/project names, provision URL. URL resolution: {name}-provision-url if non-empty; else http://localhost:{port} when an explicit host port was given to AddGlitchTip; else fail fast naming both remedies.
  2. Readiness poll: GET {url}/api/0/ until 200, ~5-minute timeout (first boot runs migrations), progress via ctx.Logger.
  3. Provision: GlitchTipProvisioner.ProvisionAsync(...)(dsnKey, projectId); ensure-style calls are idempotent.
  4. Persist: write to deployment state sections Parameters:{name}-dsn-key / Parameters:{name}-project-id (AcquireSectionAsyncSetValueSaveSectionAsync — on the manager, not the section). Skip the save when values are unchanged.
  5. Report: ctx.Summary.Add("GlitchTip", ...) — org/project names only, never the key (secret). When values were newly written, log clearly: “DSN provisioned and saved; run aspire deploy again (or restart consumers) to apply it.”

Failures throw → pipeline fails with the step's message; re-running is safe.


Changes to CommunityToolkit.Aspire.GlitchTip (client package)

Map a DSN without key/project-id (http://@glitchtip:8000/) to an empty Sentry:Dsn so the SDK is disabled rather than throwing in SentrySdk.Init. Required for the first-deploy window in both options A and B.


Explicitly out of scope / removed

  • Env-file patching + second ComposeUpAsync — compose-specific; violates the agnostic requirement.
  • Provisioner sidecar/job container, image publishing, stdout log-marker watching, WaitForCompletion contract — removed with the CI-side HTTP model.
  • Single-deploy convergence — impossible today without publisher-specific hacks; tracked upstream (aspire-issue-draft.md). When Aspire grows a deferred-value mechanism, option B's step becomes its natural producer.
  • If option B is dropped entirely (acceptable fallback), everything above minus the step still ships: run-mode auto-provisioning + manual deploy-mode parameters.

Files affected

File Change
GlitchTipResource.cs Internal DsnKeyParameter/ProjectIdParameter; branch ConnectionStringExpression
GlitchTipBuilderExtensions.cs Publish-mode parameters; optional WithDeployTimeProvisioning() wiring (step factory + guarded ordering)
GlitchTipProvisioner.cs Extract (dsnKey, projectId) core overload; keep run-mode wrapper
GlitchTipDeployStep.cs New — readiness poll, HTTP provisioning, deployment-state save
CommunityToolkit.Aspire.GlitchTip Key-less DSN → disabled Sentry
README.md Deploy docs: parameter contract, manual flow, optional auto-provisioning, two-deploy convergence note
Tests Connection-string branching, parameter round-trip, step guards/idempotency, client DSN guard
api/*.cs GenAPI baselines Regenerate if WithDeployTimeProvisioning() is added (other additions are internal)

Resolved decisions (as implemented)

  1. Endpoint exposure: WithDeployTimeProvisioning() marks the http endpoint external (it needs the published port for automatic URL resolution, and operators need the UI). Option A leaves exposure to the user.
  2. Opt-in: option B ships as WithDeployTimeProvisioning(), a no-op in run mode. Option A users never pay the polling timeout.
  3. Ordering: only docker-compose-up-{env} gets a guarded DependsOn edge; no helm-deploy-{env} name guessing. Non-compose targets rely on the step's readiness polling and must set {name}-provision-url.
  4. Endpoint auto-resolution (compose): the step reads ProjectName/OutputPath from the DockerCompose:{env} state section and queries IContainerRuntime.ComposeListServicesAsync for the published port of the GlitchTip service — the same primitive Aspire's own PrintEndpointsAsync uses — yielding http://localhost:{publishedPort} with {name}-provision-url as the override.

GlitchTip Integration — Design Space & Risk Analysis

Source spike: ../aspire-glitchtip/
Date: 2026-06-09


Agreed Design Decisions

# Decision Choice
1 Package scope Two packages: hosting + client
2 Package names CommunityToolkit.Aspire.Hosting.GlitchTip + CommunityToolkit.Aspire.GlitchTip
3 Resource type GlitchTipResource : ContainerResource, IResourceWithConnectionString
4 Connection string value ReferenceExpression combining provisioned DsnKey/ProjectId fields with EndpointReference for host and port; provisioner sets the fields in ResourceReadyEvent handler — no host rewriting needed
5 Client config section Aspire:GlitchTip / Aspire:GlitchTip:{connectionName}
6 Provisioning Auto inside the hosting package via ResourceReadyEvent; deploy mode deferred — will use the deploy pipeline API, not the legacy publishing manifest
7 Dependency ownership User passes Postgres and Redis in via .WithPostgres(pg) / .WithRedis(cache) — this is the primary and only API; no auto-creation
8 Connection string injection Standard WithReference(glitchtip) — injects ConnectionStrings__<name> into the target
9 Health check WithHttpHealthCheck("/api/0/") — no custom IHealthCheck class needed

Design Space

Dimensions

Dimension Values considered Chosen
Package scope Hosting only / Hosting + client / Hosting + documented convention Hosting + client
Client naming GlitchTip-branded / Sentry-branded GlitchTip-branded (SDK is an impl detail)
Connection string surface IResourceWithConnectionString / raw env var / both IResourceWithConnectionString only — avoids ambiguity
Connection string shape Literal rewritten DSN / ReferenceExpression with endpoint refs ReferenceExpression — host/port resolved per consumer; provisioner populates DsnKey + ProjectId fields
Provisioning location Inside package / left to AppHost Inside package (zero-config)
Dependency ownership Package owns / user passes / package owns + With-override User passes via .WithPostgres / .WithRedis — matches Umami + Keycloak convention in this repo
Health check Custom IHealthCheck class / WithHttpHealthCheck(path) WithHttpHealthCheck("/api/0/") — no custom class needed
Injection ergonomics WithReference only / custom WithGlitchTip / both WithReference (standard Aspire idiom)
Deploy mode Run-mode only / deploy pipeline step / both Run mode only for v1; deploy mode deferred — will use deploy pipeline API (not legacy manifest)

Unexplored / explicitly ruled-out regions

Region Why not chosen
Sentry-branded client (CommunityToolkit.Aspire.Sentry) SDK is an impl detail; naming by server is the established pattern in this repo
Separate Dsn property alongside connection string Ambiguity — single surface (connection string) is cleaner
WithGlitchTip convenience method for injection Standard WithReference is sufficient; no custom env var key mapping needed
Pure env var injection (no IResourceWithConnectionString) Loses dashboard visibility and deploy pipeline integration
Literal rewritten DSN as connection string Requires RewriteDsnHost; wrong for container consumers; not manifest-compatible; replaced by ReferenceExpression with endpoint references
Package-owned Postgres + Redis (auto-creation) Breaks the established convention in this repo (Umami, Keycloak both require user to pass the DB in)
Custom IHealthCheck class with URL factory WithHttpHealthCheck("/api/0/") handles endpoint resolution natively
Legacy publishing manifest for deploy mode Deploy mode will target the deploy pipeline API; manifest generation not the integration point

Package API sketch

Hosting package

// Wire up dependencies first, then add GlitchTip
var postgres = builder.AddPostgres("postgres").AddDatabase("glitchtip-db");
var redis    = builder.AddRedis("redis");

var glitchtip = builder.AddGlitchTip("glitchtip")
    .WithPostgres(postgres)
    .WithRedis(redis);

// Consuming service
builder.AddProject<Projects.Api>("api")
    .WithReference(glitchtip)
    .WaitFor(glitchtip);

GlitchTipResource shape:

public class GlitchTipResource : ContainerResource, IResourceWithConnectionString
{
    // Populated by the provisioner in ResourceReadyEvent
    public string? DsnKey     { get; internal set; }
    public string? ProjectId  { get; internal set; }

    public EndpointReference PrimaryEndpoint { get; }

    // ReferenceExpression: Aspire resolves host/port per consumer and per mode.
    // DsnKey/ProjectId are set before any consumer evaluates this (WaitFor guarantees it).
    public ReferenceExpression ConnectionStringExpression =>
        ReferenceExpression.Create(
            $"https://{DsnKey}@{PrimaryEndpoint.Property(EndpointProperty.Host)}:{PrimaryEndpoint.Property(EndpointProperty.Port)}/{ProjectId}");
}

AddGlitchTip internally:

  1. Creates the GlitchTipResource container with all required env vars
  2. Calls .WithHttpHealthCheck("/api/0/") — no custom health check class
  3. Subscribes to ResourceReadyEvent to run GlitchTipProvisioner, which sets DsnKey and ProjectId on the resource

Parameters exposed (all overridable via Parameters:* config):

Parameter Default Secret Purpose
<name>-secret-key generated yes SECRET_KEY container env var
<name>-admin-email admin@dev.local no Bootstrap admin account + provisioner auth
<name>-admin-password Admin1234! yes Bootstrap admin account + provisioner auth
<name>-org-name <name> no Org created/ensured by provisioner
<name>-project-name <name> no Project created/ensured by provisioner
<name>-from-email email@example.com no DEFAULT_FROM_EMAIL container env var

Client package

// Program.cs in a service
builder.AddGlitchTipClient();                          // uses connectionName "glitchtip"
builder.AddGlitchTipClient("monitoring");              // named connection
builder.AddKeyedGlitchTipClient("secondary");          // keyed DI

Reads ConnectionStrings:<connectionName> (DSN), configures the Sentry .NET SDK. Config section Aspire:GlitchTip:{connectionName} for future settings (sample rate, etc.).


FMEA — Runtime Failure Modes

Scope: provisioning flow + DSN injection. Scoring: Severity (S), Occurrence (O), Detection (D), RPN = S×O×D.

# Component Failure mode Effect S O D RPN Current control (spike) Recommended mitigation
1 Health check Wrong endpoint (/api/0/_health/ does not exist in v6) Health check never passes; dependent services never start 9 6 3 162 Spike uses /api/0/ WithHttpHealthCheck("/api/0/"); add integration test
2 Auth — CSRF rotation Pre-rotation CSRF token sent to /api/0/api-tokens/ 403; no API token; provisioning fails 8 5 4 160 Spike reads CSRF after login Always call GetCsrfToken() after session is established, not before
3 DSN host rewriting DSN contains internal container address; host services can't reach it Sentry SDK sends events to unreachable URL; silently dropped resolved Eliminated: ConnectionStringExpression uses EndpointReference for host/port; Aspire resolves per consumer. RewriteDsnHost not needed.
4 API idempotency — org Duplicate org creation returns 403 Provisioner fails on re-run; DsnKey/ProjectId not set 7 6 3 126 Spike does GET-before-POST Check-before-create pattern; treat 200 on GET as success
5 API idempotency — team Duplicate team creation returns 500 Provisioner fails on re-run 7 6 3 126 Spike lists teams and checks slug List + slug match; treat existing as success
6 API idempotency — project Duplicate project silently appends numeric suffix to slug ProjectId set to wrong slug; DSN fetched for wrong project 8 6 8 384 Spike lists projects and matches org+slug List + (org slug, project slug) match; never POST if found
7 ResourceReadyEvent handler failure Exception in provisioner; event handler swallows it DsnKey/ProjectId stay null; WithReference injects empty string; Sentry SDK disabled silently 7 4 7 196 None in spike Catch and log; throw DistributedApplicationException to fail fast and surface in dashboard
8 Provisioner called without allocated endpoint GlitchTipResource.PrimaryEndpoint URL null when provisioner runs NullReferenceException; provisioner cannot call GlitchTip API 8 2 4 64 Spike uses ResourceEndpointsAllocatedEvent first Aspire guarantees endpoints are allocated before ResourceReadyEvent fires; read the allocated URL from PrimaryEndpoint at handler start; guard with assertion
9 Wrong GLITCHTIP_DOMAIN value Container env var set to external URL instead of internal URL Outgoing requests from container use wrong host 5 4 6 120 Spike sets GLITCHTIP_DOMAIN to glitchhttp (endpoint reference) Set GLITCHTIP_DOMAIN to the internal endpoint reference, not the external URL
10 GlitchTip v6 auth endpoint change Future GlitchTip version changes allauth URL or drops browser mode Auth flow broken; no API token 7 3 5 105 None Pin container image tag (not latest); integration test against pinned version; document tested version in README
11 Admin password strength Weak default password rejected by GlitchTip's validator Signup fails; provisioning fails 5 3 4 60 Default is Admin1234! (passes) Document password policy; validate at parameter resolution time
12 Postgres not ready when GlitchTip starts GlitchTip container starts, migrations fail, container exits or loops Health check never passes 7 4 3 84 Spike uses WaitFor(postgres) .WithPostgres must call .WaitFor(database) on the GlitchTip resource builder
13 API token label collision Multiple Aspire runs create multiple aspire-provisioner tokens Token list grows; functionally harmless but messy 2 7 9 126 None GET /api/0/api-tokens/ before creating; re-use existing token with matching label if found
14 DsnKey/ProjectId null when consumer env vars resolved WithEnvironment callback captures null fields if provisioner hasn't run yet Sentry SDK disabled in service 9 2 5 90 Spike: WaitFor blocks service start WaitFor(glitchtip) ensures service doesn't start until ResourceReadyEvent handlers complete; document this requirement
15 Project list pagination GET /api/0/projects/ returns only first page; duplicate project not found Idempotency check misses existing project; auto-increment suffix fires; ProjectId wrong 8 3 7 168 None in spike Confirm GlitchTip returns all projects in one response; if paginated, follow next links until exhausted

High-priority items (RPN > 150)

# Item RPN Action
3 DSN host rewriting resolved Eliminated by ReferenceExpression with endpoint references
6 Project slug auto-increment 384 List+match check before any project creation; guard against pagination (see #15)
7 Provisioner exception swallowed 196 Fail fast with DistributedApplicationException
15 Project list pagination 168 Confirm single-page response or implement next-link following
1 Wrong health endpoint 162 WithHttpHealthCheck("/api/0/"); integration test
2 CSRF rotation 160 Read CSRF after session establishment

Open questions

  1. WithPostgres / WithRedis complexity — resolved: user passes dependencies in; no auto-creation.
  2. Deploy pipeline API scope — deploy mode targets the deploy pipeline API (not the legacy manifest). The key open sub-questions are: (a) how does the deploy step obtain and persist the DsnKey/ProjectId provisioned values, and (b) in deploy mode DsnKey/ProjectId are null at manifest build time — does the deploy step substitute them as parameter values, or inject the full DSN literal? Decide before implementing deploy support.
  3. Image tag to pin — spike uses glitchtip/glitchtip:6. Confirm exact digest or major.minor tag before shipping. Check https://glitchtip.com/documentation/install for the current recommended tag.
  4. SERVER_ROLE=all_in_one vs split roles — spike uses all-in-one. Multi-worker deployment (web + worker + beat) is out of scope for v1 but should be noted in README.
  5. Project list pagination — does GET /api/0/projects/ return all projects in a single response? Verify against the OpenAPI spec at https://app.glitchtip.com/api/openapi.json and GlitchTip source before shipping EnsureProjectAsync.

References

Resource URL
GlitchTip documentation https://glitchtip.com/documentation
GlitchTip install guide https://glitchtip.com/documentation/install
GlitchTip getting started https://glitchtip.com/documentation/getting-started
GlitchTip OpenAPI spec https://app.glitchtip.com/api/openapi.json
Spike implementation ../aspire-glitchtip/GlitchTipDemo.AppHost/GlitchTipSetup.cs
Aspire source reference ../aspire/src/Aspire.Hosting/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment