Problem or Use Case
The current Repository API has three entry points with overlapping concerns:
Repository.connect(name, region, namespace=...) — connect to existing infrastructure
Repository.builder(name, region).namespace(...).build() — provision + connect
Repository(name, region, ...) — deprecated direct constructor
This creates onboarding friction:
- Three competing paths — developers don't know which to use
- Namespace is buried as a keyword arg or builder method, but it's the most important parameter for most users
- No auto-provision — users must know whether infrastructure exists before choosing
connect() vs builder()
- Version check skipped for local endpoints — blunt workaround instead of checking the version record
Proposed Solution
1. Repository.open() — the one path
Namespace is the primary positional argument. Stack name defaults to "zae-limiter" and is rarely needed:
# Most users
repo = await Repository.open("my-app")
# Multi-tenant
repo_alpha = await Repository.open("tenant-alpha")
repo_beta = await Repository.open("tenant-beta")
# Explicit stack
repo = await Repository.open("my-app", stack="custom-stack", region="eu-west-1")
# Absolute simplest (stack="zae-limiter", namespace="default")
repo = await Repository.open()
# LocalStack
repo = await Repository.open("my-app", endpoint_url="http://localhost:4566")
Signature:
@classmethod
async def open(
cls,
namespace: str = "default",
*,
stack: str | None = None,
region: str | None = None,
endpoint_url: str | None = None,
config_cache_ttl: int = 60,
auto_update: bool = True,
) -> "Repository":
Stack resolution: stack arg → ZAE_LIMITER_STACK env var → "zae-limiter"
Auto-provision behavior (always, no flag):
- Try to resolve namespace → done (fast path)
InfrastructureNotFoundError → deploy stack with defaults, register namespace
NamespaceNotFoundError → register namespace, resolve
- Version check + auto-update (always, including local endpoints)
2. Fully fluent builder() — zero args
For power users who need custom infrastructure options (permission boundary, Lambda memory, etc.):
# Minimal
repo = await Repository.builder().namespace("my-app").build()
# Enterprise
repo = await (
Repository.builder()
.stack("custom-stack")
.region("us-east-1")
.namespace("my-app")
.permission_boundary("arn:aws:iam::aws:policy/PowerUserAccess")
.role_name_format("PowerUserPB-{}")
.policy_name_format("PowerUserPB-{}")
.lambda_memory(512)
.build()
)
New builder methods: .stack(), .region(), .endpoint_url() (previously positional args).
Defaults mirror open(): stack from env var or "zae-limiter", namespace "default".
3. Remove local endpoint special-casing
Current: if not endpoint_url: guards around version check and Lambda auto-update.
Fix: Remove all endpoint_url guards. The version record already handles this:
lambda_version: None → no Lambda → requires_lambda_update = False (already works)
- Version check runs everywhere (AWS, LocalStack, any endpoint)
4. connect() removal
connect() was added in #381 and has not been released. No deprecation needed — just remove it and replace with open().
Migration path
| Before |
After |
Repository.connect("app", "us-east-1") |
Repository.open(stack="app") |
Repository.connect("app", "us-east-1", namespace="x") |
Repository.open("x", stack="app") |
Repository.builder("app", "us-east-1").build() |
Repository.open(stack="app") |
Repository.builder("app", "us-east-1").namespace("x").build() |
Repository.open("x", stack="app") |
Repository.builder("app", "us-east-1").lambda_memory(512).build() |
Repository.builder().stack("app").lambda_memory(512).build() |
Acceptance Criteria
Repository.open()
Repository.builder()
Cleanup
Tests
Documentation
Problem or Use Case
The current Repository API has three entry points with overlapping concerns:
Repository.connect(name, region, namespace=...)— connect to existing infrastructureRepository.builder(name, region).namespace(...).build()— provision + connectRepository(name, region, ...)— deprecated direct constructorThis creates onboarding friction:
connect()vsbuilder()Proposed Solution
1.
Repository.open()— the one pathNamespace is the primary positional argument. Stack name defaults to
"zae-limiter"and is rarely needed:Signature:
Stack resolution:
stackarg →ZAE_LIMITER_STACKenv var →"zae-limiter"Auto-provision behavior (always, no flag):
InfrastructureNotFoundError→ deploy stack with defaults, register namespaceNamespaceNotFoundError→ register namespace, resolve2. Fully fluent
builder()— zero argsFor power users who need custom infrastructure options (permission boundary, Lambda memory, etc.):
New builder methods:
.stack(),.region(),.endpoint_url()(previously positional args).Defaults mirror
open(): stack from env var or"zae-limiter", namespace"default".3. Remove local endpoint special-casing
Current:
if not endpoint_url:guards around version check and Lambda auto-update.Fix: Remove all
endpoint_urlguards. The version record already handles this:lambda_version: None→ no Lambda →requires_lambda_update = False(already works)4.
connect()removalconnect()was added in #381 and has not been released. No deprecation needed — just remove it and replace withopen().Migration path
Repository.connect("app", "us-east-1")Repository.open(stack="app")Repository.connect("app", "us-east-1", namespace="x")Repository.open("x", stack="app")Repository.builder("app", "us-east-1").build()Repository.open(stack="app")Repository.builder("app", "us-east-1").namespace("x").build()Repository.open("x", stack="app")Repository.builder("app", "us-east-1").lambda_memory(512).build()Repository.builder().stack("app").lambda_memory(512).build()Acceptance Criteria
Repository.open()open(namespace="default", *, stack=None, region=None, endpoint_url=None, config_cache_ttl=60, auto_update=True)existsstackarg →ZAE_LIMITER_STACKenv var →"zae-limiter"SyncRepository.open()generated with matching signatureRepository.builder()builder()takes zero args.stack(),.region(),.endpoint_url()open(): stack from env/default, namespace"default"SyncRepositoryBuildergeneratedCleanup
connect()removed fromRepository(unreleased, no deprecation)connect()removed fromSyncRepository(generated)repository.py:220-221— removeif not endpoint_urlguard on version checkrepository.py:1106— removenot self.endpoint_urlguard on Lambda updaterepository.py:1146— removecan_auto_update=not self.endpoint_url__init__.pymodule docstring updated withopen()examplesopen()as recommended, remove "skip for local" references, remove.enable_aggregator(False)from LocalStack exampleopen()Tests
open()fast path (infra + namespace exist)open()auto-provision (infra missing)open()auto-register namespace (infra exists, namespace missing).stack(),.region(),.endpoint_url()connect()tests migrated toopen()Documentation
docs/getting-started.mdupdated withopen()as primary entry pointdocs/api/repository.mdupdated:open()reference, builder zero-arg signaturedocs/api/index.mdupdated withopen()examplesdocs/guide/basic-usage.mdupdated withopen()examplesdocs/infra/deployment.mdupdated:open()vsbuilder()guidance, removeconnect()docs/infra/production.mdupdated withopen()multi-tenant examplesdocs/contributing/localstack.mdupdated: remove aggregator special-casingdocs/performance.mdupdated if it referencesconnect()orbuilder(name, region)