FlexGalaxy Resource Names (FRN)

An FRN identifies a resource inside the FlexGalaxy.AI authorization model. FRNs play the same role AWS ARNs play in IAM: policy statements name resources with FRNs, authorization requests carry a target resource FRN, and audit events can record the affected resource.

Format

frn:{account-id}:{service}:{resource-path}

Four colon-separated segments, never five:

Segment

Description

Example

frn

Literal prefix. Always exactly frn.

frn

{account-id}

Owning account identifier, or * in policy patterns.

acc-029cea77800e, acc-1, *

{service}

Service namespace.

iam, devices, licensing, audit, org, idc, keycloak, example-service, s3, profile

{resource-path}

Slash-delimited resource hierarchy. The resource type is part of this path.

user/u-001, device/dev-001, bucket/reports/2026.csv

An FRN parser splits on : into exactly four parts. A value like frn:dotid:acc-029cea77800e:user:alice is invalid because it has five colon-separated parts. Put the resource kind inside {resource-path} instead: frn:acc-029cea77800e:iam:user/alice.

Service segment registry

{service} is reserved by an authoritative service team. Adding a new segment without registration here is forbidden — two services on the same segment collide on policy resource patterns and audit-event resource fields.

Segment

Owner

Notes

iam

DotID — IAM core

Users, groups, policies, access keys

org

DotID — Organization service

Organizations, OUs, delegation

idc

DotID — Identity Center

Permission sets, account assignments

audit

DotID — Audit service

Audit events

keycloak

DotID — Keycloak proxy

Realm + keycloak-mediated resources

s3

DotID — Generic resource example

Example/documentation resource shape

profile

DotID — Profile service

User profile data

devices

DotID — example IoT capability

Reserved name for future devices capability

licensing

DotID — example licensing capability

Reserved name for future licensing capability

example-service

DotID — examples / smoke tests

Test fixtures only — not a real service

To reserve a new segment, open a PR against this file with a justification for the segment (per-resource vs per-capability split, naming alignment with the IAM action namespace, etc.).

Examples

These examples match FRNs used by the PDP tests and service code:

frn:acc-029cea77800e:iam:user/alice
frn:acc-029cea77800e:iam:group/platform-admins
frn:acc-029cea77800e:iam:policy/pol-7d3b9f12
frn:acc-029cea77800e:iam:access-key/AKIA-9F3B-12C8
frn:acc-target:licensing:device/dev-001
frn:acc-target:audit:event/ev-001
frn:acc-target:org:ou/ou-001
frn:acc-target:idc:permissionset/ps-001
frn:acc-target:keycloak:realm/r-001
frn:acc-1:s3:bucket/reports/2026.csv

The account segment is the owning account, even when another account is making the request.

Policy patterns

Concrete resource FRNs should identify one resource. Policy statement resource patterns may use wildcards:

frn:*:iam:user/*
frn:acc-1:devices:device/*
frn:acc-1:s3:bucket/**

* matches one segment value or one resource-path part. ** is only meaningful inside the resource path, where it matches deeper slash-delimited paths.

Parsing FRNs in your service

Keep the same four-segment shape wherever you parse FRNs:

def parse_frn(s: str):
    parts = s.split(":", 3)
    if len(parts) != 4 or parts[0] != "frn":
        raise ValueError(f"not a valid FRN: {s}")
    _, account_id, service, resource_path = parts
    return account_id, service, resource_path
public record Frn(String accountId, String service, String resourcePath) {
    public static Frn parse(String s) {
        String[] parts = s.split(":", 4);
        if (parts.length != 4 || !"frn".equals(parts[0])) {
            throw new IllegalArgumentException("not a valid FRN: " + s);
        }
        return new Frn(parts[1], parts[2], parts[3]);
    }
}

Valid segment characters are intentionally conservative: account and service segments allow letters, digits, _, ., *, and -; the resource path also allows /.

Cross-account FRNs

When a policy in account A references a resource in account B, the FRN’s {account-id} is B, the owning account, not the caller. Cross-account access is denied by default. To enable it, the owning account must publish an explicit trust policy naming the calling account, and the calling identity must still pass its own account’s identity-side checks. Without a matching trust policy, PDP returns decision: DENY, reason: CROSS_ACCOUNT_NO_TRUST.

What FRNs are not

FRNs are identifiers, not URLs. They do not dereference over HTTP, and there is no GET frn:... endpoint.

To look up the resource behind a DotID FRN, call the owning service’s public API using the account-scoped route. For example:

GET /identity/v1/accounts/{accountId}/users/{userId}
GET /audit/v1/accounts/{accountId}/events

Other service namespaces use their own public APIs. Treat FRNs as opaque authorization identifiers you pass around, not as fetchable URLs.

Empty-account-id form (org-delegation exception)

The empty-{account-id} segment form

frn::{service}:{resource-path}

is RESERVED exclusively for resources whose lifecycle does not belong to any single account. The canonical first instance is the org-wide service-delegation pointer:

frn::example-service:org-delegation/{org-id}

This is the FRN the authorization service evaluates when a registered service needs to know which account holds delegated-administrator authority for that service in an organization.

Empty account-id is NEVER a wildcard

Wildcards are * only. A pattern with empty {account-id} matches ONLY a target with empty {account-id}; a pattern with * does NOT match a target with empty {account-id}. The matcher treats the empty account-id as its own distinct segment value.

Concretely:

Pattern

Target

Matches?

frn::example-service:org-delegation/*

frn::example-service:org-delegation/u-1

TRUE — empty-to-empty

frn:*:example-service:org-delegation/*

frn::example-service:org-delegation/u-1

FALSE — * is not a synonym for empty

frn:acc-xxx:example-service:*

frn::example-service:org-delegation/u-1

FALSE — explicit account-id does not match empty

frn::example-service:org-delegation/*

frn:acc-1:example-service:org-delegation/u-1

FALSE — empty pattern does not match populated target

Parsing the empty account-id

When you parse this form, the {account-id} segment is the empty string "". If your code branches on whether an FRN has an owning account, treat an empty account-id as its own distinct case — not as a missing value and not as a wildcard.

When to use this form

Adding another instance of the empty-{account-id} form requires explicit justification (“which other resource has a lifecycle that does not belong to any single account?”); do not silently extend the convention to ad-hoc resources.

See also