Skip to main content

The Registry Module Planning Session (part 1)

·6650 words·32 mins

Introduction
#

Up to this point, we have focused on building primitive modules like DNS, ACM, S3, and CloudFront. As those pieces stabilize, the need for a registry becomes clearer: we need a consistent way to track what exists, where it lives, and who is responsible for it.

Now that we have a design direction, we can jump right into the implementation planning. In this session, we want to start translating the design discussion into something we can actually build by:

  • narrowing down requirements
  • figuring out what will be easy/hard
  • start assessing infra implementation details
  • try to make some decisions about what the registry’s interfaces will look like
  • assess the outcomes of the design session (do we need to rethink anything)

That’s a lot, and we need to move through it in a thorough and intentional way. The overall goal is to establish a high level development plan that we can run with. We don’t want to get too specific yet — the details will reveal themselves as development progresses. We do, however, want to know the general direction and plan to make sure we don’t miss anything along the way.

One way to think about this session is like planning a car trip. We know the final destination, we know that there will be sites, food, gas etc. along the way. Things like tourist attractions are similar to ‘must haves’ so we need to plan our trip accordingly around those items. While things like gas and food are also very important, we can assume that there will be food and gas along the way and we can figure out what we want to eat and where we want to get gas in real time as we need them. So these “must haves” can be figured out when we get to them as opposed to trying to figure them out ahead of time. Other details like how to get to a specific address once we arrive in the destination city are even less important to preplan. These details will be sorted as we navigate our way to the final destination. To sum up, we know where we want to go, we know there are some things we have to plan out before we really get going, we know there are other things we need to figure out while we are travelling and there will be some minor details that will need to get sorted as we get closer to our final destination.

A loose plan
#

We can think about the registry itself as the final destination. We will likely need a few new primitive modules to get us there which will need to be planned out carefully. We will also need to glue everything together, the scope of which will become apparent when we get there. We can break the whole project down into a number of phases:

  1. module skeleton - create the repo with a standard dxaws layout - manager, planner, executor and providers base and aws. Will also include a standard test suite.
  2. core modules and enums - This is where we define: object types, relationship types, lifecycle states, control modes and roles. No real logic here yet, just creating stable types
  3. manager surface - Now implement: object registration. object lookup, linking, listing children/parents, state transition validation. This proves the object graph
  4. principals and grants - Add principal registration, role grants, basic access checks and simple inheritance from parent object
  5. assignment workflow - the workflow for an object might be something like: available → reserved → assigned → active. Want to release back to available or maybe suspend/retire. This will prove the warm-pool model
  6. website integration - have dxaws-website register: a site, bucket, certificate, distribution, dns objects, owner relationships.

Design Statements
#

Next we will summarize the design goals of the project. The idea here is to have a single root of truth for the project. This documentation also helps keeping our coding agents in line, giving them context when they are making decisions.

Purpose
#

The registry serves as the control plane for the dxaws ecosystem. Its job is to provide a consistent, authoritative source of truth describing what objects exist, where they live, how they relate to each other, and who is allowed to interact with them.

As the platform grows beyond simple one-off infrastructure deployments, we quickly run into questions that cannot be answered by any individual primitive module:

  • Which resources belong to a given site?
  • Which AWS account and region contain those resources?
  • Who owns the site?
  • Who is allowed to modify its content?
  • Which objects were created but are not yet assigned to a user?
  • Which infrastructure is platform-managed versus customer-managed?
  • Which resources should be cleaned up when a site is retired?

Without a registry, each module would need to answer these questions independently, resulting in duplicated logic and inconsistent assumptions about ownership, lifecycle, and permissions.

The registry solves this by introducing a stable identity layer above the infrastructure itself. Objects in the registry represent logical entities such as sites, buckets, certificates, distributions, DNS records, applications, and users. These objects are connected through relationships that describe ownership, usage, and assignment.

Importantly, registry objects are not tied directly to a single AWS account or region. Instead, location is treated as metadata associated with an object. This allows infrastructure to move, be reassigned, or be attached from external environments without breaking the logical identity of the object. This also means that objects can be moved out of our system to third party accounts while still allowing us to track them.

This distinction allows the platform to support:

  • multi-account deployments
  • multi-region deployments
  • pre-built infrastructure pools
  • reassignment of resources between users
  • integration with customer-owned AWS resources
  • clean separation between logical ownership and physical location

In effect, the registry provides a unified way to reason about the system as a whole. Primitive modules continue to manage infrastructure resources, but the registry keeps track of how those resources fit together.

As the platform evolves, additional modules such as content publishing, administrative interfaces, and application hosting will rely on the registry to determine:

  • what resources exist
  • how they are connected
  • who is allowed to use them
  • where they are located

Most importantly, establishing this layer early, we avoid embedding identity and ownership logic inside individual modules and instead create a shared foundation that supports consistent behavior across the entire system. We really do not want to have modules worrying about these things.

Core Concepts
#

The registry models the system as a graph of logical objects. Each object represents something meaningful to the platform, such as a site, bucket, certificate, distribution, DNS record, application, or user. Objects are connected through relationships that describe ownership, usage, and assignment.

The goal of the model is not to replicate AWS resource definitions, but rather to describe how infrastructure components fit together at a system level.

Primitive modules are responsible for managing infrastructure resources. The registry is responsible for describing how those resources relate to each other and who is allowed to interact with them.

Objects
#

An object is the fundamental unit tracked by the registry.

Objects represent logical entities within the platform, not just infrastructure resources. Some objects map directly to AWS resources, while others represent higher-level concepts that do not exist natively in AWS.

Examples of objects include:

  • site
  • surface
  • bucket
  • distribution
  • certificate
  • dns_record
  • application
  • principal (user, team, or service identity)

Each object has:

  • a stable dxaws identifier
  • a type
  • a lifecycle state
  • optional location metadata (account, region, provider)
  • optional provider identifiers (such as an ARN or resource ID)
  • optional ownership metadata
  • arbitrary metadata defined by the managing module

The object identifier is stable and does not change even if the underlying infrastructure moves between accounts or regions.

Object Identity
#

Each object is assigned a unique identifier within the dxaws system.

Identifiers are structured but location-independent:

dxaws:<object-type>:<id>

Examples:

dxaws:site:0001
dxaws:bucket:0001
dxaws:distribution:0001
dxaws:certificate:0001
dxaws:dns_record:0001
dxaws:surface:0001
dxaws:principal:tenant-acme

Provider-specific identifiers such as AWS ARNs are stored as metadata associated with the object rather than forming part of the object’s identity.

This allows objects to be reassigned, migrated, or attached to external infrastructure without changing their logical identity.

Relationships
#

Objects rarely exist in isolation. Relationships describe how objects are connected.

Examples:

  • a site owns a bucket
  • a site owns a distribution
  • a distribution uses a certificate
  • a certificate uses dns records for validation
  • a site is assigned to a principal
  • a site uses a customer-owned bucket

Relationships are directional and typed.

Common relationship types include:

  • owns
  • uses
  • assigned_to
  • manages
  • depends_on

Relationships allow the system to understand how infrastructure components fit together without embedding that logic inside individual modules.

For example, a site object may own:

  • a public surface
  • a preview surface
  • an admin surface

Each surface may reference its own origin or application resources.

Principals
#

Principals represent actors within the system.

A principal may represent:

  • a user
  • a team
  • a tenant
  • an automated service
  • a future application identity

Principals may be granted permissions on objects.

Permissions are defined independently of object ownership, allowing fine-grained control over who may view, modify, publish, or administer different parts of the system.

Location
#

Some objects correspond to infrastructure resources that exist in specific AWS accounts and regions.

Location is stored as metadata associated with the object:

  • cloud provider
  • account identifier
  • region
  • provider resource identifier
  • provider resource id e.g., ARN (if applicable)

Location is not part of the object identity.

This allows infrastructure to move without changing the logical identity of the object.

Examples:

  • a bucket may move from a platform account to a customer account
  • content storage may be attached from an external AWS account
  • resources may be deployed across multiple regions

The registry tracks where objects live, but does not define their identity based on location.

Control Modes
#

Not all objects are managed in the same way.

The registry tracks the level of control the platform has over an object.

Common control modes include:

  • platform_managed
  • customer_managed
  • shared
  • external_reference

Control mode determines what actions automation is allowed to perform on an object.

For example:

  • platform-managed objects may be created, updated, or deleted by the system
  • customer-managed objects may be referenced but not modified
  • shared resources may have limited modification rules

Lifecycle States
#

Objects move through lifecycle states over time.

Examples:

  • provisioning
  • available
  • reserved
  • assigned
  • active
  • suspended
  • recycling
  • retired
  • error

Lifecycle state helps the system determine whether an object may be assigned, modified, or reused.

Lifecycle transitions are validated by the registry manager to ensure consistent behavior across modules.

Object Graph
#

Taken together, objects and relationships form a graph describing the structure of the platform.

Example:

site owns → public surface owns → preview surface owns → distribution owns → bucket owns → dns records

distribution uses → certificate

certificate uses → dns validation records

site assigned_to → tenant principal

This graph allows higher-level modules to reason about the system without needing to understand provider-specific implementation details.

The registry does not replace primitive modules. Instead, it provides a shared structural layer that allows modules to coordinate consistently.

Metadata Model
#

Objects may include arbitrary metadata defined by the managing module. Metadata allows modules to store configuration or descriptive attributes that do not affect object identity or lifecycle state.

Examples:

site metadata:

  • display_name
  • theme
  • default_locale

bucket metadata:

  • versioning_enabled
  • encryption_type

distribution metadata:

  • caching_profile
  • compression_enabled

Metadata is:

  • optional
  • namespaced by module where appropriate
  • not part of object identity
  • not used directly for authorization decisions
  • expected to evolve over time

The registry guarantees storage and retrieval of metadata but does not interpret metadata values.

Object Type Registry
#

Object types are defined centrally to ensure consistency across modules.

Initial types may include:

  • site
  • surface
  • application
  • bucket
  • distribution
  • certificate
  • dns_record
  • principal

Additional types may be introduced as new modules are added.

Modules should avoid inventing new types dynamically without registry awareness.

Keeping object types centrally defined allows the graph to remain interpretable across modules.

Relationship Constraints
#

Relationships define how objects connect within the registry graph. While the system is intentionally flexible, some structural expectations help keep the graph understandable.

Examples:

  • objects may have multiple relationships
  • some relationship types may be one-to-many
  • cyclic relationships should generally be avoided
  • structural validation rules may be introduced at the manager layer

Example expectations:

site:

  • may own multiple surfaces
  • may own multiple infrastructure objects

principal:

  • may be assigned multiple objects

certificate:

  • may be used by one or more distributions

These constraints may evolve as real usage patterns emerge.

Registry Events
#

The registry will emit events when significant control-plane changes occur.

Examples include:

  • object registered
  • relationship created or removed
  • lifecycle state changed
  • assignment changed
  • grant created or revoked
  • location updated
  • control mode updated

Events allow external systems such as observability, automation, or audit pipelines to react to changes in registry state.

The exact event transport mechanism will be defined separately from the registry itself.

Lifecycle Model
#

Objects tracked by the registry move through a defined set of lifecycle states. Lifecycle states describe the operational readiness of an object and determine what actions are allowed at a given point in time.

The lifecycle model allows the platform to support workflows where infrastructure is created ahead of time, assigned to users later, and eventually retired or recycled when no longer needed.

Lifecycle state is stored as part of the object record and is validated by the registry manager when transitions occur.

The goal is to provide a predictable and consistent way for modules to understand whether an object is ready for use, currently in use, or no longer available.

Why lifecycle matters
#

Primitive infrastructure modules generally focus on converging resources into a desired technical configuration. The registry extends this idea by tracking the operational readiness of logical objects over time.

Examples:

  • a website may be created before a user signs up
  • a site may be reserved during onboarding
  • a site may be actively used for months or years
  • a site may be temporarily suspended
  • a site may eventually be retired
  • infrastructure may be recycled into a warm pool

Without a lifecycle model, each module would need to independently determine whether an object should be modified, reused, or destroyed.

A shared lifecycle model ensures consistent behavior across modules.

Lifecycle states
#

The initial lifecycle states are designed to support warm-pool provisioning and assignment workflows.

provisioning

The object is being created or configured by a primitive module. The object may not yet be usable.

Examples:

  • a bucket is being created
  • a certificate is being validated
  • a distribution is deploying
  • DNS records are propagating

Objects should not be assigned or used while in this state.

available

The object has been successfully provisioned and is ready to be assigned.

Examples:

  • a pre-built website waiting for a user
  • an unused preview environment
  • an unassigned storage resource

Objects in this state may be reserved or assigned.

reserved

The object has been temporarily allocated but is not yet fully active.

Examples:

  • a site is being configured for a new user
  • onboarding is in progress
  • DNS changes are pending

Reserved objects are not available to other principals.

assigned

The object has been associated with a principal but may not yet be actively used.

Examples:

  • a site has been allocated to a tenant
  • ownership has been recorded
  • configuration is being finalized

active

The object is in normal operational use.

Examples:

  • a public website is live
  • preview content is available
  • applications are deployed

Most objects will spend the majority of their life in this state.

suspended

The object is temporarily disabled but not deleted.

Examples:

  • subscription expired
  • policy violation
  • administrative pause
  • customer temporarily disables a site

Suspended objects retain their relationships and configuration but may not be publicly accessible.

recycling

The object is being prepared for reuse.

Examples:

  • content is being removed
  • permissions are being reset
  • configuration is returning to baseline

This state supports warm-pool workflows where infrastructure is reused rather than destroyed. An example may be an AWS (or other provider) account where reusing the account is much more effective than deleting and creating a new account.

Implementation details for recycling may evolve over time.

retired

The object is no longer in use and will not be reassigned.

Examples:

  • infrastructure intentionally decommissioned
  • object archived for audit purposes

Retired objects may still exist in the registry for historical tracking.

error

The object is in an unexpected or inconsistent state.

Examples:

  • provisioning failed
  • configuration drift detected
  • provider resource missing
  • permissions mismatch

Error states allow operators or automation to intervene before further actions occur.

Lifecycle transitions
#

Lifecycle transitions are validated by the registry manager.

Example transition paths:

provisioning → available available → reserved reserved → assigned assigned → active

active → suspended suspended → active

active → recycling recycling → available

active → retired

Any transition to error may occur if an operation fails validation.

Not all object types will use all lifecycle states. Primitive infrastructure objects such as DNS records may move directly from provisioning to active.

The lifecycle model provides a shared vocabulary that allows modules to coordinate behavior without embedding workflow logic inside each module.

Relationship to primitive modules
#

Primitive modules remain responsible for converging infrastructure resources.

The registry lifecycle does not replace infrastructure convergence. Instead, it describes the operational readiness of logical objects that depend on those resources.

For example:

A certificate may be considered active once DNS validation has completed and the provider reports an issued status.

A distribution may be considered active once deployment has completed.

A site may be considered active once its required infrastructure dependencies are active.

Higher-level modules may use lifecycle state to determine whether additional actions should be performed.

Assignment Model
#

The registry supports workflows where objects are created before they are attached to a specific user, tenant, or service. Assignment is the process of connecting one of these logical objects to a principal and making it available for use within the system.

This is especially important for infrastructure that may be provisioned ahead of time, such as pre-built websites, preview environments, or reusable platform resources. By separating provisioning from assignment, the platform can prepare infrastructure in advance and reduce the amount of work required during onboarding.

Assignment is treated as a first-class control-plane action. It is not simply a matter of adding an owner field to an object record. Assignment changes the operational meaning of the object and may trigger lifecycle transitions, permission inheritance, and additional configuration steps.

Why assignment matters
#

In a simple system, infrastructure is created only when a user needs it. In a larger system, that approach can become slow, expensive, and operationally awkward.

The registry allows the platform to support workflows such as:

  • creating websites before any user is attached to them
  • reserving a site during onboarding
  • assigning a site to a tenant once signup is confirmed
  • releasing or recycling a site after a user leaves
  • attaching new users to pre-existing platform objects
  • connecting external resources that are already owned by a customer

This separation between provisioning and assignment gives the platform more flexibility while keeping ownership and lifecycle state explicit.

Assignment as a relationship
#

Assignment is modeled as a relationship between an object and a principal.

Example:

dxaws:site:0001 assigned_to dxaws:principal:tenant-acme

This relationship indicates that the site has been allocated to the tenant and is no longer part of the unassigned pool.

Assignment is distinct from lower-level object ownership relationships such as:

dxaws:site:0001 owns dxaws:bucket:0001
dxaws:site:0001 owns dxaws:distribution:0001

These object-to-object relationships describe system structure. Assignment describes tenancy or operational responsibility.

Assignment states
#

Assignment works closely with the lifecycle model.

A common assignment path for a site may look like:

available → reserved → assigned → active

These states have slightly different meanings:

available

The object exists and is ready to be assigned, but currently has no principal attached.

reserved

The object is temporarily held while onboarding, configuration, or validation is in progress. Reserved objects are not available to other principals.

assigned

The object has been attached to a principal, but may not yet be fully activated.

active

The object is in normal use by the assigned principal.

This progression allows the platform to distinguish between inventory that is merely ready, inventory that is in the process of being claimed, and infrastructure that is actually in service.

Reservation
#

Reservation exists to support workflows where assignment is not instantaneous.

For example:

  • a user signs up and the platform selects a pre-built site
  • DNS or naming configuration still needs to be completed
  • permissions must be applied
  • content or application settings may need to be initialized

During this period, the site should not be allocated to anyone else, but it may not yet be ready to use. The reserved state captures this intermediate condition.

Reservation may also include timeout or expiration behavior in the future. For example, a reserved site could be returned to the available pool if onboarding does not complete.

Ownership and assignment
#

Assignment does not necessarily mean that the assigned principal directly owns every underlying resource.

Instead, assignment usually applies to a top-level logical object such as a site. Lower-level resources are typically related through object ownership:

  • tenant is assigned to site
  • site owns bucket
  • site owns distribution
  • site owns certificate
  • site owns DNS records

This distinction matters because it allows permissions and responsibility to be inherited through the object graph rather than copied onto every individual resource.

For example, assigning a site to a tenant may grant that tenant operational control over the site as a whole, while the platform still retains direct management of the underlying AWS resources.

Assignment and permissions
#

Assignment and permissions are related, but they are not the same thing.

Assignment answers the question:

Which principal is this object currently attached to?

Permissions answer the question:

What is this principal allowed to do with the object?

In many cases, assignment will trigger default grants.

For example, assigning a site to a tenant may automatically grant:

  • owner or admin access on the site
  • inherited access to child surfaces
  • limited operational permissions on related resources

This keeps the assignment model simple while allowing the permissions model to remain flexible.

Reassignment
#

Because registry identity is stable, objects may be reassigned without changing their logical identifiers.

Examples:

  • a pre-built website moves from unassigned inventory to a tenant
  • a suspended site is transferred to a different owner
  • infrastructure is released, cleaned, and returned to the available pool
  • a customer-managed resource is detached or reattached elsewhere in the graph

Reassignment should be an explicit operation validated by the registry manager. The registry should not silently change assignment in place without recording the event.

This preserves auditability and makes it possible to understand the lifecycle of an object over time.

Assignment workflow examples

A typical platform-managed onboarding flow might look like:

  1. a site is provisioned and enters available
  2. the site is selected for onboarding and moves to reserved
  3. the site is assigned to a tenant principal
  4. permissions and related configuration are applied
  5. the site transitions to active

A release workflow might look like:

  1. a site leaves normal service
  2. it enters suspended or recycling
  3. assignment is removed
  4. permissions are reset
  5. the site is either returned to available or moved to retired

An external attachment workflow may look slightly different:

  1. a customer-owned bucket is registered
  2. the bucket is linked to a site through a uses relationship
  3. control mode is marked as customer-managed or external-reference
  4. assignment remains on the site, not necessarily on the bucket itself

Relationship to the manager
#

Assignment is validated and applied by the registry manager.

The manager is responsible for:

  • ensuring the object is in a valid state for assignment
  • preventing multiple conflicting assignments
  • recording the relationship between the object and the principal
  • triggering lifecycle transitions where appropriate
  • applying any default grants or inheritance rules

This keeps assignment behavior consistent across modules and prevents higher-level components from inventing their own ownership logic.

Summary
#

The assignment model allows the platform to distinguish between:

  • infrastructure that merely exists
  • infrastructure that is temporarily reserved
  • infrastructure that is attached to a principal
  • infrastructure that is actively in use

This distinction is central to the registry design.

By treating assignment as a first-class control-plane operation, the platform can support warm-pool provisioning, onboarding workflows, reassignment, and eventual recycling without losing track of identity, ownership, or permissions.

Permissions Model
#

The registry must do more than track what objects exist and who they are assigned to. It also needs to answer a second, equally important question:

What is a given principal allowed to do with a given object?

This is the role of the permissions model.

Permissions are a control-plane concern. They allow the platform to distinguish between ownership, assignment, and operational authority. A principal may be assigned to a site, but that does not automatically imply unrestricted access to every underlying resource. In the same way, the platform itself may retain administrative control over infrastructure even when a tenant has been granted the ability to manage content or site settings.

The permissions model exists to make these distinctions explicit.

Why permissions matter
#

As the platform grows, different types of actors will need different levels of access.

Examples include:

  • tenants managing their own sites
  • platform administrators managing the system as a whole
  • automated services publishing content
  • preview systems generating draft versions of a site
  • internal tools inspecting resources without being allowed to modify them

Without a clear permissions model, these distinctions would need to be embedded in higher-level modules, leading to duplicated logic and inconsistent enforcement.

A shared permissions model allows all modules to reason about authority in a consistent way.

Principals and objects
#

Permissions are granted from a principal to an object.

A principal represents an actor in the system, such as:

  • a user
  • a team
  • a tenant
  • an automated service
  • an application identity

An object represents something tracked by the registry, such as:

  • a site
  • a surface
  • an application
  • a bucket
  • a distribution
  • a certificate
  • a DNS record

A permission grant describes the relationship between the two.

Example:

dxaws:principal:tenant-acme has admin on dxaws:site:0001

This indicates that the tenant principal has administrative access to the site object.

Permissions are distinct from assignment

Assignment and permissions are related, but they are not the same thing.

Assignment answers the question:

Which principal is this object currently attached to?

Permissions answer the question:

What actions may this principal perform on this object?

This distinction is important because assignment often applies only at the top level of the object graph, while permissions may need to be inherited, restricted, or overridden at lower levels.

For example:

  • a tenant may be assigned to a site
  • that assignment may result in admin access to the site
  • child surfaces may inherit a subset of that access
  • underlying infrastructure resources may remain platform-managed

This lets the platform delegate control in a controlled and predictable way.

Roles
#

Permissions are granted using roles rather than individual ad hoc actions.

A role groups together a set of expected capabilities. This keeps the model simple while still allowing it to grow over time.

The initial role set may include:

  • owner
  • admin
  • editor
  • publisher
  • viewer
  • operator

These roles are intentionally broad.

For example:

owner

The highest level of authority for a logical object. Owners may manage access, configuration, and assignment.

admin

Administrative control over the object and its immediate behavior, but not necessarily over every underlying provider resource.

editor

May modify content or application settings, but may not manage ownership or permissions.

publisher

May publish or promote content, generate preview output, or trigger deployment-related workflows.

viewer

Read-only access to object metadata and related system information.

operator

Used by internal automation or platform services that need controlled operational access.

The exact meaning of each role may vary slightly by object type, but the registry should provide a stable default interpretation.

Grants
#

Permissions are stored as grants.

A grant links:

  • a principal
  • a role
  • an object

Example:

principal = dxaws:principal:tenant-acme
role = admin
object = dxaws:site:0001

Grants may be created:

  • explicitly by platform administrators
  • automatically during assignment
  • automatically during onboarding or provisioning workflows

For example, assigning a site to a tenant may automatically create an owner or admin grant for that tenant on the site.

This allows assignment and permissions to remain distinct while still working together naturally.

Inheritance
#

Because the registry models the system as an object graph, permissions should be able to inherit through that graph.

A common pattern may be:

  • tenant has admin on site
  • site owns public surface
  • site owns preview surface
  • site owns application resources

In this case, child objects may inherit permissions from the site unless a more specific rule overrides them.

This avoids the need to duplicate grants across every related object.

For example, instead of granting access separately to:

  • site
  • public surface
  • preview surface
  • bucket
  • distribution

the platform may grant access to the site and allow child objects to derive their effective permissions from that relationship.

This keeps the model manageable while preserving structure.

Explicit overrides
#

Inheritance should not be the only mechanism.

There will be cases where a child object needs more restrictive or more specific access than its parent.

Examples:

  • a tenant may administer the site but not the underlying certificate
  • an editor may modify preview content but not publish to production
  • a platform operator may inspect a customer-managed object without being allowed to mutate it

For these cases, the registry should support explicit grants on child objects.

This allows the system to combine broad inherited defaults with targeted exceptions.

Control modes and permissions

Permissions must also respect the control mode of the object.

For example:

  • platform_managed objects may permit full platform automation
  • customer_managed objects may permit only limited access
  • external_reference objects may be visible in the graph but not directly modifiable by the platform

This means that a grant alone does not necessarily imply unrestricted action. The registry may need to combine:

  • the principal
  • the assigned role
  • the target object
  • the object’s control mode

to determine whether an operation is valid.

This is especially important for workflows involving customer-owned infrastructure or resources attached from outside the platform.

Effective permissions
#

The registry should be able to answer not only what grants exist, but what permissions are effectively in force for a principal on an object.

Effective permissions may be derived from:

  • direct grants
  • inherited grants
  • assignment-related default grants
  • control mode restrictions
  • future policy rules

This makes the registry useful to higher-level modules, which should not need to reimplement permission logic themselves.

Examples of useful registry questions include:

  • Can this principal edit this site?
  • Can this service publish preview content?
  • Can this operator modify this bucket?
  • Does this tenant have access to this surface?

Permissions and lifecycle
#

Permissions may also depend on lifecycle state.

Examples:

  • an available site should not yet be editable by a tenant
  • a reserved object may only be accessible to onboarding workflows
  • a suspended site may still be visible to administrators but not to public users
  • a recycling object may be inaccessible except to internal automation

This does not mean lifecycle state replaces permissions. Rather, lifecycle places operational boundaries around when permissions may be exercised.

The effective result is that both lifecycle and grants contribute to the final authorization decision.

Relationship to the manager
#

Permissions are validated by the registry manager.

The manager is responsible for:

  • recording grants
  • revoking grants
  • resolving inherited permissions
  • evaluating effective access
  • ensuring permission checks respect lifecycle and control mode
  • preventing inconsistent or conflicting permission state

This keeps the permission model centralized and prevents higher-level modules from inventing their own authorization rules.

Summary
#

The permissions model allows the registry to distinguish between:

  • who an object is assigned to
  • who owns it structurally
  • who may act on it operationally

This distinction is essential for a system that supports:

  • pre-built infrastructure pools
  • multi-tenant onboarding
  • inherited object relationships
  • customer-managed resources
  • automation services
  • future administrative and publishing workflows

By treating permissions as a first-class registry concern, the platform gains a consistent way to reason about authority across the entire object graph.

Location and Control Modes
#

The registry is intended to track objects across multiple AWS accounts, multiple AWS regions, and potentially across infrastructure that is not directly owned by the platform. Because of this, the registry must distinguish between an object’s logical identity and its physical location.

This distinction is one of the core design decisions of the module.

An object in the registry has a stable dxaws identity such as:

dxaws:site:0001
dxaws:bucket:0001
dxaws:distribution:0001

That identity does not change simply because the underlying infrastructure moves between accounts, is reassigned to a customer-owned environment, or is attached from an external system.

Instead, location is stored as metadata associated with the object.

Why location matters
#

As the platform grows, infrastructure will not always live in a single AWS account or even in a single administrative boundary.

Examples include:

  • sites built in different workload accounts
  • resources deployed in multiple regions
  • ACM certificates that must exist in a specific region for CloudFront
  • objects transferred out to customer-owned AWS accounts
  • objects attached from infrastructure that already exists outside the platform

Without a shared location model, each module would need to independently discover where resources live before it could act on them. This leads to duplicated logic, inconsistent assumptions, and unnecessary coupling between modules.

The registry solves this by recording location as part of the object record.

Location metadata
#

Objects that correspond to infrastructure resources may include location metadata such as:

  • cloud provider
  • account identifier
  • region
  • provider namespace or service
  • provider-specific resource identifier
  • provider-native resource id such as an ARN, when applicable

Examples:

A platform-managed bucket:

  • cloud provider: aws
  • account id: 111122223333
  • region: ca-central-1
  • provider service: s3
  • provider resource id: platform-site-bucket-0001

A CloudFront certificate:

  • cloud provider: aws
  • account id: 111122223333
  • region: us-east-1
  • provider service: acm
  • provider resource id: arn:aws:acm:us-east-1:111122223333:certificate/…

A customer-owned archive bucket:

  • cloud provider: aws
  • account id: 444455556666
  • region: us-west-2
  • provider service: s3
  • provider resource id: customer-image-archive

This information tells the platform where the resource lives, but it does not define what the object is. The logical identity remains stable even if the location changes.

Location is not identity
#

The registry intentionally keeps location separate from identity.

For example, this object:

dxaws:bucket:0001

may begin life as a platform-managed bucket in one AWS account and later be transferred to a customer-owned account. The bucket’s location metadata would change, but the logical object could still remain part of the registry for tracking, auditing, or relationship purposes.

This separation allows the platform to support:

  • migration between AWS accounts
  • migration between regions
  • attachment of external infrastructure
  • long-lived object history that survives location changes
  • consistent identity across operational transitions

If account and region were encoded directly into the canonical object identifier, then moves or transfers would create identity churn throughout the object graph. By keeping identity stable and location separate, the registry remains easier to reason about over time.

Control modes
#

Location alone is not enough to determine what the platform may do with an object.

Two objects may both live in AWS and still have very different management boundaries. For that reason, the registry also tracks a control mode for each object.

Control mode describes how much authority the platform has over the object.

Common control modes include:

  • platform_managed
  • customer_managed
  • shared
  • external_reference

These values describe the operational relationship between the object and the platform.

Platform-managed
#

A platform-managed object is created, updated, and deleted by the platform.

Examples:

  • a pre-built site bucket
  • a CloudFront distribution created by the platform
  • Route 53 DNS records in a platform account

These objects are fully under platform control and may be modified by automation according to the object’s lifecycle state and permissions model.

Customer-managed
#

A customer-managed object belongs to a customer environment even if it participates in the registry graph.

Examples:

  • a customer-owned S3 bucket used for large image archives
  • resources the platform may inspect or connect to, but not destroy
  • infrastructure transferred to a customer account during offboarding

These objects may still be represented in the registry and linked to platform-managed objects, but the platform should not assume unrestricted mutation rights over them.

Shared
#

A shared object is used across multiple logical tenants or services and may have limited or carefully controlled mutation rules.

Examples:

  • shared infrastructure used by more than one site
  • common platform services
  • internal systems that support multiple objects in the registry graph

Shared objects may allow some platform actions while restricting tenant-level actions or reassignment behavior.

External reference
#

An external reference is an object that the registry knows about but does not directly manage.

Examples:

  • an upstream content source
  • a third-party system identifier
  • a resource recorded for dependency tracking or auditing

External references may appear in the object graph so that other modules can reason about them, but they should generally not be treated as mutable infrastructure.

Why control modes matter
#

Control mode affects what actions are valid for a given object.

For example:

  • a platform-managed object may be created, updated, or recycled by automation
  • a customer-managed object may be readable and linkable, but not deletable by the platform
  • a shared object may require stricter rules around reassignment
  • an external reference may exist only for lookup and coordination

This means that a valid action depends on more than just the object type or the permissions granted to a principal. It also depends on whether the platform actually controls the object.

This becomes especially important in workflows involving:

  • customer-owned infrastructure
  • cross-account integrations
  • transfers during offboarding
  • resources reused across multiple sites or services

Location, control, and the object graph
#

Location and control mode both contribute to how objects participate in the registry graph.

For example:

dxaws:site:0001 owns dxaws:bucket:0001
dxaws:site:0001 owns dxaws:distribution:0001
dxaws:site:0001 uses dxaws:bucket:customer-archive

In this example:

  • the first bucket may be platform-managed and live in a platform account
  • the distribution may live in a different region
  • the customer archive bucket may live in a customer account and be marked customer-managed

All three objects can appear in the same graph, but the registry still preserves the differences in location and operational authority.

This allows higher-level modules to reason about the full system while still respecting management boundaries.

Relationship to the manager
#

The registry manager is responsible for validating and enforcing the meaning of location and control mode.

The manager should:

  • record location metadata as part of object registration
  • allow location to change without changing logical object identity
  • prevent invalid actions against objects whose control mode forbids them
  • expose enough location information for higher-level modules to make decisions
  • preserve the distinction between platform-managed and externally managed infrastructure

This keeps location and control semantics centralized and prevents higher-level modules from encoding their own assumptions about where objects live or what may be done to them.

Deletion Model
#

Objects are generally not immediately removed from the registry.

Instead, objects typically transition to:

retired

Retaining historical records allows the platform to preserve:

  • audit history
  • lifecycle traceability
  • relationship context
  • safe cleanup workflows

Hard deletion may exist for test objects or invalid records but should be treated as an exceptional operation rather than a normal lifecycle step.

Summary
#

The registry separates logical identity from physical location.

This allows objects to remain stable even when infrastructure moves between accounts, regions, or management boundaries.

By combining location metadata with explicit control modes, the registry can represent:

  • platform-managed infrastructure
  • customer-managed infrastructure
  • shared resources
  • external references

This gives the rest of the dxaws ecosystem a consistent way to answer two essential questions:

Where is this object?

What is the platform allowed to do with it?

Closing
#

This feels like a good stopping point for the first part of the registry planning session. We now have a solid high-level model for what the registry needs to track, how objects relate to each other, and how the system can reason about ownership, permissions, lifecycle, and location. That is enough structure to move into the next stage of planning. In the next post, we will start working through the manager interface surface and figure out what the public entry points to the registry should look like.