Another grabbag of updates, focused on multi-tenancy, access control, rate limiting, and a dashboard UI.

tenants and members

The demo app now has tenants – namespaces for resources. Users can create tenants, invite members, and manage membership. If you invite someone who doesn't have an account yet, the invitation is stored as "pending" and resolved when they register. Each user has a personal tenant.

type TenantType string

const (
	TenantPersonal     TenantType = "personal"
	TenantOrganization TenantType = "organization"
)

type Tenant struct {
	ID   core.ID
	Name string
	Type TenantType
}

type MemberStatus string

const (
	MemberInvited MemberStatus = "invited"
	MemberActive  MemberStatus = "active"
)

type TenantMember struct {
	TenantID core.ID
	UserID   core.ID
	Status   MemberStatus
}

roles and grants

Started on a simple RBAC model. Roles are collections of actions. Groups are collections of users. Grants assign a role to a principal (user or group).

// RoleID is a globally unique role identifier that encodes its namespace
// as the first path segment. System roles use the reserved "app" namespace
// (e.g. "app.editor"). Custom tenant roles use the tenant ID as
// the namespace (e.g. "01ABC...XYZ.custom_editor").
//
// RoleID must have at least two segments: <namespace>.<name>
type RoleID string

type Role struct {
	ID      RoleID
	Name    string
	Actions []Action
}

type Grant struct {
	PrincipalID core.ID
	RoleID      RoleID
}

type Group struct {
	ID   core.ID
	Path core.Path
	Name string
}

By convention, roles can be namespaced to a specific tenant by prefixing the role ID (see RoleID above) with the tenant ID. This convention allows system-wide roles to easily exist in the app.* namespace.

rate limiting

The core/throttle package provides a token bucket implementation:

type TokenBucket interface {
	Take(ctx context.Context, key string, capacity int, refillRate float64) (bool, error)
	Reset(ctx context.Context, key string) error
}

Four backends: in-memory, sqlite, postgres, and redis. The first use case in the demo app is login throttling – limiting login attempts per email to slow down brute force attacks.

dashboard

The demo app now has a dashboard and admin UI, built with Mithril.js and backed by RPC endpoints. It's the first real exercise of the routes.RPC approach in the demo. Users can manage tenants, members, groups, roles, and grants. The admin panel provides a system-wide view.

smaller things

  • core/dbi got Paginate and ScanAll helpers to reduce boilerplate.

what's next

The access control story needs more work – connecting roles and grants to the Guard interface, adding permission checks, and figuring out how it all works with paths and hierarchies.

Thoughts? Email blog@atlas9.dev.