Workstream B/a: quota middleware + workspace lifecycle + egress templates#4
Merged
Conversation
New `internal/quota/` package implements a thread-safe per-tenant token-bucket Limiter (rate + burst). The `internal/server/quota_mw.go` middleware consumes one token per request, reading the bearer-bound tenant via tenant.FromContext (mirroring tenants_admin.go); on deny it returns 429 with a Retry-After header and the standard httperr "rate_limited" envelope. Tests cover allow/deny/refill/burst (bucket_test.go) and middleware integration with httptest including the 429 + Retry-After contract (quota_mw_test.go). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds named egress bundles (github-only, pypi+npm, wide-open, default- aliasing-github-only) in `internal/egress/templates.go`. Each bundle carries a list of allowed FQDNs and a default network stance (deny | allow). Unknown / empty names fall back to "default". `internal/server/egress_templates.go` defines the EgressTemplateProvider interface the future workspace-create handler consumes, plus a defaultEgressTemplateProvider that materializes a bundle as positive-priority egress.Rule rows (1000+i, descending) so seeded template rules outrank the hard-coded baseline allow-list. Tests cover template lookup, fallback to default, case-insensitivity, and the canonical bundle set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ive)
Adds `internal/server/workspaces_lifecycle.go` with four endpoints
under /api/v1/tenants/{tid}/workspaces:
POST / — create (seeds egress allowlist from the
request body's `egress_template`, defaulting
to "default" which aliases "github-only")
DELETE /{wid} — soft-delete (sets DeletedAt; 30-day window)
POST /{wid}:restore — restore (rejected once retention expires)
POST /{wid}:archive — vault-shred, S3 manifest upload, fs remove,
finalize ArchivedAt
Tenant resolution mirrors tenants_admin.go (bearer-bound id via
tenant.FromContext; path {tid} must match the bearer or be the
DefaultTenant). Soft-delete and restore audit via EventTenantUpdate
with an "event" payload discriminator; archive emits the new
EventWorkspaceArchived Kind (added to internal/server/audit/audit.go)
for each phase (vault_shred → s3_upload → fs_remove → finalize).
S3Uploader is an interface with a FakeS3Uploader test double; real
client wiring is out of scope. VaultDeleter narrows the local
secrets.Vault surface to List+Delete so the archive path can shred
keys without depending on the full vault.
Tests cover create / soft-delete / restore / archive happy paths and
the unauthorized tenant rejection (cross-tenant DELETE → 403).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves conflict in internal/server/router.go between this branch (quotaLimiter, egressTemplates, workspaceLifecycle) and wsB-b's already-merged additions (usageAggregator, orgGates, adminEmails, adminSessionLister, adminAuditPubKey, adminAuditRecords). Both sides are additive struct fields; union resolve preserves both. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Workstream B slice (a) of gemba-remote M4 — Productization.
Test plan
Branched off local `main` at `684227c`.
🤖 Generated with Claude Code