Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 62 additions & 49 deletions plans/plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@
// promised" (no scheduled backups on this tier). FIX-H #Q50 (B36).
RPOMinutes int `yaml:"rpo_minutes"`

// RTOMinutes — Recovery Time Objective. The target wall-clock

Check warning on line 100 in plans/plans.go

View workflow job for this annotation

GitHub Actions / typos

"RTO" should be "TO".
// duration between "operator presses restore" and "data is back
// online" for a tier. Includes the worker tick + pg_restore +
// post-restore verification. 0 means "RTO not promised" (no

Check warning on line 103 in plans/plans.go

View workflow job for this annotation

GitHub Actions / typos

"RTO" should be "TO".
// self-serve restore available on this tier). FIX-H #Q50 (B36).
RTOMinutes int `yaml:"rto_minutes"`

Check warning on line 105 in plans/plans.go

View workflow job for this annotation

GitHub Actions / typos

"rto" should be "to".

Check warning on line 105 in plans/plans.go

View workflow job for this annotation

GitHub Actions / typos

"RTO" should be "TO".

// VectorStorageMB is the maximum storage per pgvector-enabled Postgres
// database in megabytes. Mirrors PostgresStorageMB because pgvector
Expand Down Expand Up @@ -575,8 +575,10 @@
mongodb_storage_mb: 5
mongodb_connections: 2
mongodb_ops_per_minute: 100
queue_storage_mb: 1024
queue_count: -1
# strict-80% margin redesign (2026-06-05): queue 1024 → 64 MB;
# queue_count -1 → 1 (0 means unlimited via QueueCountLimit, so 1).
queue_storage_mb: 64
queue_count: 1
storage_storage_mb: 10
webhook_requests_stored: 100
team_members: 1
Expand Down Expand Up @@ -612,8 +614,9 @@
mongodb_storage_mb: 5
mongodb_connections: 2
mongodb_ops_per_minute: 100
queue_storage_mb: 1024
queue_count: -1
# strict-80% margin redesign (2026-06-05): mirror anonymous.
queue_storage_mb: 64
queue_count: 1
storage_storage_mb: 10
webhook_requests_stored: 100
team_members: 1
Expand Down Expand Up @@ -644,7 +647,8 @@
mongodb_storage_mb: 100
mongodb_connections: 5
mongodb_ops_per_minute: 1000
queue_storage_mb: 5120
# strict-80% margin redesign (2026-06-05): queue 5120 → 2048 MB.
queue_storage_mb: 2048
queue_count: 3
storage_storage_mb: 512
webhook_requests_stored: 1000
Expand Down Expand Up @@ -769,7 +773,8 @@
mongodb_storage_mb: 100
mongodb_connections: 5
mongodb_ops_per_minute: 1000
queue_storage_mb: 5120
# strict-80% margin redesign (2026-06-05): queue 5120 → 2048 MB (mirror hobby).
queue_storage_mb: 2048
queue_count: 3
storage_storage_mb: 512
webhook_requests_stored: 1000
Expand Down Expand Up @@ -802,7 +807,8 @@
mongodb_storage_mb: 5120
mongodb_connections: 20
mongodb_ops_per_minute: 10000
queue_storage_mb: 10240
# strict-80% margin redesign (2026-06-05): queue 10240 → 5120 MB.
queue_storage_mb: 5120
queue_count: 20
storage_storage_mb: 51200
webhook_requests_stored: 10000
Expand Down Expand Up @@ -836,7 +842,8 @@
mongodb_storage_mb: 5120
mongodb_connections: 20
mongodb_ops_per_minute: 10000
queue_storage_mb: 10240
# strict-80% margin redesign (2026-06-05): queue 10240 → 5120 MB (mirror pro).
queue_storage_mb: 5120
queue_count: 20
storage_storage_mb: 51200
webhook_requests_stored: 10000
Expand All @@ -858,24 +865,27 @@
display_name: "Team"
price_monthly_cents: 19900
limits:
# strict-80% margin redesign (2026-06-05): every -1 retired to finite
# caps (mirrors api/plans.yaml). Above Team = Enterprise/contact-sales.
# Team stays GATED (no checkout). Hard-caps only.
provisions_per_day: -1
postgres_storage_mb: -1
postgres_connections: -1
vector_storage_mb: -1
vector_connections: -1
redis_memory_mb: -1
redis_commands_per_day: -1
mongodb_storage_mb: -1
mongodb_connections: -1
mongodb_ops_per_minute: -1
queue_storage_mb: -1
queue_count: -1
storage_storage_mb: -1
webhook_requests_stored: -1
team_members: -1
vault_max_entries: -1
postgres_storage_mb: 51200
postgres_connections: 100
vector_storage_mb: 30720
vector_connections: 100
redis_memory_mb: 1536
redis_commands_per_day: 10000000
mongodb_storage_mb: 40960
mongodb_connections: 50
mongodb_ops_per_minute: 50000
queue_storage_mb: 40960
queue_count: 100
storage_storage_mb: 307200
webhook_requests_stored: 100000
team_members: 25
vault_max_entries: 1000
vault_envs_allowed: []
deployments_apps: -1
deployments_apps: 100
backup_retention_days: 90
backup_restore_enabled: true
manual_backups_per_day: 1000
Expand All @@ -893,24 +903,25 @@
price_monthly_cents: 199000
billing_period: "yearly"
limits:
# strict-80% margin redesign (2026-06-05): mirror team monthly.
provisions_per_day: -1
postgres_storage_mb: -1
postgres_connections: -1
vector_storage_mb: -1
vector_connections: -1
redis_memory_mb: -1
redis_commands_per_day: -1
mongodb_storage_mb: -1
mongodb_connections: -1
mongodb_ops_per_minute: -1
queue_storage_mb: -1
queue_count: -1
storage_storage_mb: -1
webhook_requests_stored: -1
team_members: -1
vault_max_entries: -1
postgres_storage_mb: 51200
postgres_connections: 100
vector_storage_mb: 30720
vector_connections: 100
redis_memory_mb: 1536
redis_commands_per_day: 10000000
mongodb_storage_mb: 40960
mongodb_connections: 50
mongodb_ops_per_minute: 50000
queue_storage_mb: 40960
queue_count: 100
storage_storage_mb: 307200
webhook_requests_stored: 100000
team_members: 25
vault_max_entries: 1000
vault_envs_allowed: []
deployments_apps: -1
deployments_apps: 100
backup_retention_days: 90
backup_restore_enabled: true
manual_backups_per_day: 1000
Expand All @@ -928,19 +939,21 @@
limits:
provisions_per_day: -1
# 2026-05-15: bumped to stay above Pro after Pro storage bump.
# strict-80% margin redesign (2026-06-05): every -1 retired to finite
# caps (mirrors api/plans.yaml). Hard-caps only.
postgres_storage_mb: 20480
postgres_connections: 20
vector_storage_mb: 20480
vector_storage_mb: 10240
vector_connections: 20
redis_memory_mb: 1024
redis_commands_per_day: -1
mongodb_storage_mb: -1
mongodb_connections: -1
mongodb_ops_per_minute: -1
queue_storage_mb: -1
queue_count: -1
storage_storage_mb: -1
webhook_requests_stored: -1
redis_commands_per_day: 5000000
mongodb_storage_mb: 20480
mongodb_connections: 50
mongodb_ops_per_minute: 50000
queue_storage_mb: 20480
queue_count: 50
storage_storage_mb: 153600
webhook_requests_stored: 100000
team_members: 10
vault_max_entries: 200
vault_envs_allowed: []
Expand Down
8 changes: 5 additions & 3 deletions plans/plans_extra_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ func TestConnectionsLimit(t *testing.T) {

func TestTeamMemberLimit_DefaultsByTier(t *testing.T) {
r := plans.Default()
// team => -1 unlimited
if got := r.TeamMemberLimit("team"); got != -1 {
t.Errorf("team = %d, want -1", got)
// strict-80% margin redesign (2026-06-05): team.team_members is now a
// finite 25 (was -1 unlimited). The YAML value takes precedence over the
// built-in fallback in TeamMemberLimit.
if got := r.TeamMemberLimit("team"); got != 25 {
t.Errorf("team = %d, want 25", got)
}
// pro fallback default = 5 unless overridden in YAML (it may also be set
// explicitly; both are acceptable provided it's > 1).
Expand Down
8 changes: 6 additions & 2 deletions plans/plans_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,9 @@ func TestVaultMaxEntries_Tiers(t *testing.T) {
assert.Equal(t, 50, r.VaultMaxEntries("hobby_plus"),
"hobby_plus must allow 50 vault entries (mid-tier between hobby:20 and pro:200)")
assert.Equal(t, 200, r.VaultMaxEntries("pro"))
assert.Equal(t, -1, r.VaultMaxEntries("team"))
// strict-80% margin redesign (2026-06-05): team.vault_max_entries is now a
// finite 1000 (was -1 unlimited).
assert.Equal(t, 1000, r.VaultMaxEntries("team"))
}

func TestVaultEnvsAllowed_HobbyIsProductionOnly(t *testing.T) {
Expand All @@ -450,7 +452,9 @@ func TestDeploymentsAppsLimit_Tiers(t *testing.T) {
assert.Equal(t, 2, r.DeploymentsAppsLimit("hobby_plus"),
"hobby_plus must allow 2 deployment apps (doubles hobby's 1, vs pro's 10)")
assert.Equal(t, 10, r.DeploymentsAppsLimit("pro"))
assert.Equal(t, -1, r.DeploymentsAppsLimit("team"))
// strict-80% margin redesign (2026-06-05): team.deployments_apps is now a
// finite 100 (was -1 unlimited); justified by forthcoming scale-to-zero.
assert.Equal(t, 100, r.DeploymentsAppsLimit("team"))
assert.Equal(t, 50, r.DeploymentsAppsLimit("growth"),
"growth allows 50 deployment apps (wave-3 BugBash bumped from 5 → 50, matching plans.yaml)")
}
Expand Down
Loading