Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 9, 2025

Implements a hosting integration for OpenFGA, a fine-grained authorization server that supports multiple storage backends and provides both HTTP and gRPC APIs.

Implementation

  • Resource: OpenFgaResource exposes HTTP (8080) and gRPC (8081) endpoints with connection string support
  • Container: Pinned to openfga/openfga:v1.8.5 with /healthz health check
  • Datastore backends: In-memory, PostgreSQL, and MySQL via fluent extension methods
  • Configuration: Data volume mounting, experimental features, and custom port assignment

Usage

var builder = DistributedApplication.CreateBuilder(args);

// Development with in-memory storage
var openfga = builder.AddOpenFga("openfga")
    .WithInMemoryDatastore();

// Production with PostgreSQL
var postgres = builder.AddPostgres("postgres")
    .AddDatabase("openfga-db");

var openfga = builder.AddOpenFga("openfga")
    .WithPostgresDatastore(postgres)
    .WithDataVolume();

Testing

  • 12 unit tests covering resource creation, endpoint configuration, and datastore options
  • Integration test validating container startup and health check
  • Added to CI workflow for Ubuntu and Windows
Original prompt

This section details on the original issue you should resolve

<issue_title>OpenFGA hosting integration</issue_title>
<issue_description>### .NET Aspire issue link

No response

Overview

OpenFGA is a fine grained authorization solution that allows developers to build granular access control using a custom dsl and self host the FGA server.

https://openfga.dev/

Usage example

var openFga = builder.AddOpenFga("openfga");

var store = openFga.AddStore("my-store")
    .WithModelDefinition("models", Path.Join(builder.AppHostDirectory, "Models"), "fga.mod");

builder.AddProject<MyProject>("project")
    .WithEnvironment("OpenFga__Connection", store.HttpEndpoint)
    .WithEnvironment("OpenFga__Store", store)

Additional context

I have built an OpenFGA integration that is published to nuget hosted here https://github.com/maxs-rose/Aspire-OpenFGA allowing for the integration of all the current storage's that it supports (postgres, sqlite, and mysql) with a couple extra bits to allow stores to be created, populated, and otel support.

Help us help you

Yes, but only if others can assist</issue_description>

Comments on the Issue (you are @copilot in this section)

Custom agent used: Hosting Integration Creator
This agent helps users create new hosting integration in Aspire by scaffolding the correct projects and files based on user input.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI changed the title [WIP] Add OpenFGA hosting integration Add OpenFGA hosting integration Dec 9, 2025
Copilot AI requested a review from aaronpowell December 9, 2025 05:12
@aaronpowell
Copy link
Member

@maxs-rose here's a PR started based off your issue - do you want to have a look at it and give it a try to see if it's fitting with how you'd expect it to work?

@github-actions
Copy link
Contributor

github-actions bot commented Dec 9, 2025

Code Coverage

Package Line Rate Branch Rate Complexity Health
CommunityToolkit.Aspire.GoFeatureFlag 100% 97% 74
CommunityToolkit.Aspire.Hosting.ActiveMQ 78% 40% 108
CommunityToolkit.Aspire.Hosting.ActiveMQ.MassTransit 1% 0% 14
CommunityToolkit.Aspire.Hosting.Adminer 74% 50% 20
CommunityToolkit.Aspire.Hosting.Azure.Dapr 29% 7% 124
CommunityToolkit.Aspire.Hosting.Azure.Dapr.Redis 61% 34% 76
CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder 100% 100% 22
CommunityToolkit.Aspire.Hosting.Bun 96% 83% 28
CommunityToolkit.Aspire.Hosting.Dapr 60% 37% 840
CommunityToolkit.Aspire.Hosting.DbGate 94% 50% 18
CommunityToolkit.Aspire.Hosting.Deno 98% 85% 44
CommunityToolkit.Aspire.Hosting.Flagd 79% 100% 32
CommunityToolkit.Aspire.Hosting.GoFeatureFlag 88% 73% 36
CommunityToolkit.Aspire.Hosting.Golang 51% 40% 88
CommunityToolkit.Aspire.Hosting.Java 70% 75% 130
CommunityToolkit.Aspire.Hosting.JavaScript.Extensions 97% 85% 200
CommunityToolkit.Aspire.Hosting.k6 58% 12% 20
CommunityToolkit.Aspire.Hosting.Keycloak.Extensions 100% 100% 22
CommunityToolkit.Aspire.Hosting.KurrentDB 71% 75% 34
CommunityToolkit.Aspire.Hosting.LavinMQ 74% 50% 26
CommunityToolkit.Aspire.Hosting.LavinMQ.MassTransit 1% 0% 14
CommunityToolkit.Aspire.Hosting.MailPit 85% 50% 22
CommunityToolkit.Aspire.Hosting.McpInspector 77% 44% 204
CommunityToolkit.Aspire.Hosting.Meilisearch 71% 57% 58
CommunityToolkit.Aspire.Hosting.Minio 88% 75% 56
CommunityToolkit.Aspire.Hosting.MongoDB.Extensions 96% 83% 36
CommunityToolkit.Aspire.Hosting.MySql.Extensions 100% 88% 78
CommunityToolkit.Aspire.Hosting.Ngrok 52% 35% 82
CommunityToolkit.Aspire.Hosting.Ollama 65% 69% 260
CommunityToolkit.Aspire.Hosting.OpenFga 57% 67% 26
CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector 78% 61% 77
CommunityToolkit.Aspire.Hosting.PapercutSmtp 81% 50% 18
CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions 99% 89% 84
CommunityToolkit.Aspire.Hosting.Python.Extensions 45% 29% 100
CommunityToolkit.Aspire.Hosting.RavenDB 62% 48% 148
CommunityToolkit.Aspire.Hosting.Redis.Extensions 100% 71% 48
CommunityToolkit.Aspire.Hosting.Rust 94% 83% 16
CommunityToolkit.Aspire.Hosting.Solr 72% 100% 22
CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects 51% 48% 192
CommunityToolkit.Aspire.Hosting.Sqlite 89% 89% 52
CommunityToolkit.Aspire.Hosting.SqlServer.Extensions 100% 87% 78
CommunityToolkit.Aspire.Hosting.SurrealDb 55% 40% 256
CommunityToolkit.Aspire.KurrentDB 94% 92% 54
CommunityToolkit.Aspire.MassTransit.RabbitMQ 100% 100% 30
CommunityToolkit.Aspire.Meilisearch 97% 92% 68
CommunityToolkit.Aspire.Microsoft.Data.Sqlite 89% 85% 52
CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite 61% 58% 114
CommunityToolkit.Aspire.Minio.Client 90% 85% 112
CommunityToolkit.Aspire.OllamaSharp 77% 71% 132
CommunityToolkit.Aspire.RavenDB.Client 60% 53% 237
CommunityToolkit.Aspire.SurrealDb 79% 63% 78
Summary 68% (7366 / 10854) 56% (2089 / 3746) 4760

Minimum allowed line rate is 60%

Copilot AI temporarily deployed to azure-artifacts December 9, 2025 05:44 Inactive
@maxs-rose
Copy link

@maxs-rose here's a PR started based off your issue - do you want to have a look at it and give it a try to see if it's fitting with how you'd expect it to work?

Thanks for doing this!

Should get a chance to look over it later today

Copy link

@maxs-rose maxs-rose left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aaronpowell I think this is a fine start but feel like we are going to hit some of the same issues that I did in my original version.

So for both MySQL and Postgres we also need to manually run migrations after the container has started. I do this through some callbacks to allow the user to configure the migration container (https://github.com/maxs-rose/Aspire-OpenFGA/blob/main/Aspire.Hosting.OpenFga.MySql/OpenFgaBuilderMySql.cs#L26-L34) but I dont feel like that is the best solution.

To make this useful we probably also need to have an API to setup stores and populate them with the models. The store creation is fine as that can be done through the .NET client. However, OpenFGA has their own model definition schema that the .NET client cannot parse used to populate the data in the stores. I get around this by running another container (https://github.com/maxs-rose/Aspire-OpenFGA/blob/main/Aspire.Hosting.OpenFga/OpenFgaStoreBuilderExtensions.cs#L27) after the store is created. But it would be nice I think if we could somehow get around this as I really dont like having to run this one shot container.

Any suggestions happy to make the changes.

Comment on lines +93 to +99
return builder
.WithEnvironment("OPENFGA_DATASTORE_ENGINE", "mysql")
.WithEnvironment(context =>
{
context.EnvironmentVariables["OPENFGA_DATASTORE_URI"] = database.Resource.ConnectionStringExpression;
})
.WaitFor(database);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one wont quite work as the mysql connection string has an odd format (https://openfga.dev/docs/getting-started/setup-openfga/configure-openfga#mysql).

This is why in my original version I split out each storage backend into their own packages since for MySQL we would need to so something like this:

builder.OpenFgaDatastoreResource.CreateDatastore(builder)
                .WaitFor(database)
                .WithArgs("migrate")
                .WithEnvironment("OPENFGA_DATASTORE_ENGINE", "mysql")
                .WithEnvironment("OPENFGA_DATASTORE_URI",
                    $"mysql:{database.Resource.Parent.PasswordParameter}@tcp({database.Resource.Parent.PrimaryEndpoint.Property(EndpointProperty.HostAndPort)})/{database.Resource.DatabaseName}?parseTime=true");

and as far as I am aware there isnt a common interface on the database packages in aspire so we would need an IResourceBuilder<MySqlDatabaseResource> as the database here.

.WithEnvironment("OPENFGA_DATASTORE_ENGINE", "postgres")
.WithEnvironment(context =>
{
context.EnvironmentVariables["OPENFGA_DATASTORE_URI"] = database.Resource.ConnectionStringExpression;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to either use the UriExpression expression here or build the string manually since like the MySQL store it does not use a .NET like connection string (https://openfga.dev/docs/getting-started/setup-openfga/configure-openfga#postgres)

@aaronpowell
Copy link
Member

So for both MySQL and Postgres we also need to manually run migrations after the container has started. I do this through some callbacks to allow the user to configure the migration container (https://github.com/maxs-rose/Aspire-OpenFGA/blob/main/Aspire.Hosting.OpenFga.MySql/OpenFgaBuilderMySql.cs#L26-L34) but I dont feel like that is the best solution.

I think the solution you have there is pretty much on the money, maybe the configuration callback could be optional, but that's pretty minimal in the design. Reading the docs (and your integration), conceptually I map it through to the design of installers for things like Node and Python - a sidecar that you run to the primary resource.

To make this useful we probably also need to have an API to setup stores and populate them with the models. The store creation is fine as that can be done through the .NET client. However, OpenFGA has their own model definition schema that the .NET client cannot parse used to populate the data in the stores. I get around this by running another container (https://github.com/maxs-rose/Aspire-OpenFGA/blob/main/Aspire.Hosting.OpenFga/OpenFgaStoreBuilderExtensions.cs#L27) after the store is created. But it would be nice I think if we could somehow get around this as I really dont like having to run this one shot container.

Is it a case that there is no .NET types that represent the models from OpenFGA? I'm sure it could be generated from the schema using Copilot 🤣 (but obviously that has some issues that would make it sub-optimal from a long-term maintenance). The short-lived sidecars are not uncommon in Aspire, again the analogy to installer resources is very apt here (IMO).

@maxs-rose
Copy link

Is it a case that there is no .NET types that represent the models from OpenFGA?

Yes there is but the more idiomatic way to do things is with their DSL (https://openfga.dev/docs/configuration-language).

I'm sure it could be generated from the schema using Copilot 🤣

lol true

Am I good to just send up a pr targeting the copilot branch or do you think I would be better to just make a whole new one and take what copilot has done as a base?

@github-actions github-actions bot added the Stale label Dec 16, 2025
@github-actions github-actions bot closed this Dec 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenFGA hosting integration

3 participants