Skip to content

Initialize OpenFGA server on ATE API Server bootstrap. - #1670

Merged
Benjamin Elder (BenTheElder) merged 2 commits into
agent-substrate:mainfrom
SHRUTI6991:openfga_integration
Sep 18, 2026
Merged

Benjamin Elder (BenTheElder) merged 2 commits into
agent-substrate:mainfrom
SHRUTI6991:openfga_integration

Conversation

@SHRUTI6991

@SHRUTI6991 Shruti Nair (SHRUTI6991) commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Working on #1563

Summary

Initializes an embedded OpenFGA server within ateapi backed by PostgreSQL and updates the top-level authorization scope.

Key Changes

  • Rename scope to global: Renamed type cluster and parent_cluster to type global and parent_global, adding can_set_policy and can_get_policy to global.
  • Embedded OpenFGA server (internal/authz/server.go):
    • Compiles model.fga into OpenFGA's protobuf representation.
    • Runs OpenFGA PostgreSQL schema migrations (goose_db_version).
    • Serializes startup via pg_advisory_lock to avoid multi-replica races.
    • Connects OpenFGA's PostgreSQL adapter with a new dedicated *pgxpool.Pool.
    • Idempotently creates or reuses the "substrate" store and authorization model.
  • Service wiring (cmd/ateapi): Exposes Pool() on *atepg.Persistence and initializes authz.NewServer on bootstrap.

Verification

  • openfga model test: 103/103 checks passing (model_test.fga.yaml).
  • go test -mod=mod ./internal/authz/...: Passes against PostgreSQL (migrations, tuple writes, permission checks, and restart idempotency).

@SHRUTI6991
Shruti Nair (SHRUTI6991) marked this pull request as ready for review September 16, 2026 05:01
@SHRUTI6991
Shruti Nair (SHRUTI6991) force-pushed the openfga_integration branch 2 times, most recently from a0c0420 to a2cd50a Compare September 16, 2026 05:12
@SHRUTI6991

Shruti Nair (SHRUTI6991) commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator Author

Please review first commit. The other commit is vendor dependencies.

@SHRUTI6991
Shruti Nair (SHRUTI6991) marked this pull request as draft September 16, 2026 17:30
@SHRUTI6991
Shruti Nair (SHRUTI6991) force-pushed the openfga_integration branch 2 times, most recently from e16c09b to 2de21b1 Compare September 16, 2026 17:44
@SHRUTI6991
Shruti Nair (SHRUTI6991) marked this pull request as ready for review September 16, 2026 18:04
Comment thread internal/authz/server.go
server.WithDatastore(datastore),
)
if err != nil {
datastore.Close()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

IIUC, this also closes the provided *pgxpool.Pool. Maybe worth mentioning that in the NewServer() function comment and double checking to make sure that a dedicated pool is always provided here so this doesn't break anything (e.g. if it was a shared pool).

@SHRUTI6991 Shruti Nair (SHRUTI6991) Sep 17, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, I actually created a dedicated pool for openfga connections.

@SHRUTI6991 Shruti Nair (SHRUTI6991) Sep 17, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is also because, my implementation for create atespace + auth check happens in 2 different transactions since openFGA always executes in it's own transaction.

Comment thread internal/authz/server_test.go Resolved
@SHRUTI6991
Shruti Nair (SHRUTI6991) force-pushed the openfga_integration branch 2 times, most recently from 2b993f4 to 470caca Compare September 17, 2026 19:40
@bowei Bowei Du (bowei) added kind/feature An enhancement / feature request or implementation area/security Security related issue/pr area/api-machinery labels Sep 17, 2026
Comment thread cmd/ateapi/main.go Outdated
defer closer.Close()
}

if poolProvider, ok := persistence.(interface{ Pool() *pgxpool.Pool }); ok {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't think we should be creating a new connection pool for OpenFGA --- we should have one connection pool for both OpenFGA and the store (another connection pool just increases connection pressure on Postgres for no gain).

This looks like it might be a larger refactor, since we should probably be dialing the connection pools in main and injecting them into the store and OpenFGA modules. It can be done as a follow-up.

Julian Gutierrez Oschmann (@juli4n)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

working on this as followup task: #1755.

Comment thread internal/authz/server.go
"github.com/openfga/openfga/pkg/storage"
"github.com/openfga/openfga/pkg/storage/postgres"
"github.com/openfga/openfga/pkg/storage/sqlcommon"
"github.com/pressly/goose/v3"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just to clarify, this is the same solution we are using for applying migrations for the main store?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes its the same:

"github.com/pressly/goose/v3"

Comment thread internal/authz/server.go

// Server wraps an embedded OpenFGA server backed by PostgreSQL and
// initialized with Substrate's authorization model.
type Server struct {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I am not sure on whether or not we should have this wrapper struct or instead just return the *server.Server from NewServer.

Let's see some of the API call integrations, and we can revisit.

Comment thread internal/authz/server.go
storeID, modelID, err := ensureStoreAndModel(ctx, fgaServer)
unlock()
if err != nil {
fgaServer.Close()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Needing to write these lines points sort of points to that we should be creating the fgaServer and datastore in main (where they can have an unconditional defer close() after they are created).

The risk of doing this without defer is that a panic will leave them open. But obviously, it's complicated to write the defer() logic in the scope of NewServer. That's why constructor functions should usually accept their stateful dependencies as injected arguments, not try to create them inline.

I would reconsider whether this package should have a wrapper struct, or just provide utility functions to initialize the FGA server, datastore, and apply migrations.

Merged via the queue into agent-substrate:main with commit b29f977 Sep 18, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api-machinery area/security Security related issue/pr kind/feature An enhancement / feature request or implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants