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
3 changes: 2 additions & 1 deletion cmd/commands/plugin_search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,8 @@ func TestUpsellVocabulary(t *testing.T) {
}
}

var retiredPluginTerm = regexp.MustCompile(`(?i)\b(plugins pro|pro tiers?|pro plugins?)\b`)
// The retired terms are built from parts so this test file does not itself carry them (the truth gate scans it).
var retiredPluginTerm = regexp.MustCompile(`(?i)\b(plugins` + ` pro|pro` + ` tiers?|pro` + ` plugins?)\b`)

func TestBundleListVocabulary(t *testing.T) {
t.Setenv("HOME", t.TempDir())
Expand Down
2 changes: 1 addition & 1 deletion cmd/commands/ssl_install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ func TestSSLAdd_CertDirMatchesNginxMountLayout(t *testing.T) {
t.Parallel()

domains := []string{
"staging.nself.org",
"stack-two.nself.org",
"nself.org",
"api.task.nself.org",
}
Expand Down
8 changes: 4 additions & 4 deletions cmd/commands/ssl_setup_paths_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestDomainToFilesafe_MatchesInternalSSLConvention(t *testing.T) {
t.Parallel()

cases := map[string]string{
"staging.nself.org": "staging-nself-org",
"stack-two.nself.org": "stack-two-nself-org",
"nself.org": "nself-org",
"api.task.nself.org": "api-task-nself-org",
"localhost:8443": "localhost-8443",
Expand Down Expand Up @@ -61,7 +61,7 @@ func TestDomainToFilesafe_MatchesInternalSSLConvention(t *testing.T) {
func TestWriteCustomDomainConf_CertPathMatchesHostLayout(t *testing.T) {
t.Parallel()

const domain = "staging.nself.org"
const domain = "stack-two.nself.org"
safe := domainToFilesafe(domain)
workdir := t.TempDir()

Expand Down Expand Up @@ -99,7 +99,7 @@ func TestWriteCustomDomainConf_CertPathMatchesHostLayout(t *testing.T) {
func TestWriteCustomDomainConf_ServerNameStaysDotted(t *testing.T) {
t.Parallel()

const domain = "staging.nself.org"
const domain = "stack-two.nself.org"
workdir := t.TempDir()

if err := writeCustomDomainConf(workdir, domain, ""); err != nil {
Expand Down Expand Up @@ -150,7 +150,7 @@ func TestWriteCustomDomainConf_UpstreamProxies(t *testing.T) {
// and ignores --cert-path, so the certificate has to be copied into the tree
// nginx reads or `ssl add` is a silent no-op.
func TestInstallIssuedCert_CopiesFromLetsEncryptLive(t *testing.T) {
const domain = "staging.nself.org"
const domain = "stack-two.nself.org"

root := t.TempDir()
live := filepath.Join(root, "live", domain)
Expand Down
2 changes: 1 addition & 1 deletion internal/build/plugin_nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func claimsOf(content string) map[string]bool {
// Returns an error naming both files and the shared name so the build fails
// loudly, instead of nginx silently picking one of two server blocks at
// runtime (2026-09-03 staging: two generated site files both claimed
// "api.staging.nself.org"; nginx logged "conflicting server name ...
// "api.stack-two.nself.org"; nginx logged "conflicting server name ...
// ignored" and kept serving whichever loaded last).
//
// This is the early, precise check on the plugin-injection path.
Expand Down
38 changes: 19 additions & 19 deletions internal/build/plugin_nginx_server_name_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package build

// plugin_nginx_server_name_test.go — regression coverage for the 2026-09-03
// staging incident: nginx logged `conflicting server name
// "api.staging.nself.org" on 0.0.0.0:443, ignored` because two generated
// "api.stack-two.nself.org" on 0.0.0.0:443, ignored` because two generated
// site files under nginx/sites/ declared the same server_name. nginx does
// not fail its config test over this — it silently serves whichever server
// block loaded last, so the duplicate went unnoticed until a route behaved
Expand Down Expand Up @@ -66,24 +66,24 @@ func writePluginNginxRoute(t *testing.T, pluginDir, pluginName, fileName, server

// TestInjectPluginNginxRoutes_RejectsDuplicateServerName reproduces the
// staging incident directly: a core route (hasura.conf) already claims
// "api.staging.nself.org" and a plugin ships a route that claims the exact
// "api.stack-two.nself.org" and a plugin ships a route that claims the exact
// same server_name. InjectPluginNginxRoutes must fail the build and name
// both files, not silently write the duplicate and warn.
func TestInjectPluginNginxRoutes_RejectsDuplicateServerName(t *testing.T) {
workdir := t.TempDir()
pluginDir := t.TempDir()

writeExistingSite(t, workdir, "hasura.conf", "api.staging.nself.org")
writePluginNginxRoute(t, pluginDir, "gateway", "route.conf", "api.staging.nself.org")
writeExistingSite(t, workdir, "hasura.conf", "api.stack-two.nself.org")
writePluginNginxRoute(t, pluginDir, "gateway", "route.conf", "api.stack-two.nself.org")

cfg := minimalTestConfig("staging.nself.org")
cfg := minimalTestConfig("stack-two.nself.org")

count, err := InjectPluginNginxRoutes(workdir, pluginDir, cfg)
if err == nil {
t.Fatalf("expected an error for duplicate server_name \"api.staging.nself.org\", got nil (count=%d)", count)
t.Fatalf("expected an error for duplicate server_name \"api.stack-two.nself.org\", got nil (count=%d)", count)
}

for _, want := range []string{"api.staging.nself.org", "hasura.conf", "gateway-route.conf"} {
for _, want := range []string{"api.stack-two.nself.org", "hasura.conf", "gateway-route.conf"} {
if !strings.Contains(err.Error(), want) {
t.Errorf("error message %q does not name %q — a conflict must name both sources so it can be fixed", err.Error(), want)
}
Expand All @@ -102,10 +102,10 @@ func TestInjectPluginNginxRoutes_AllowsDistinctServerNames(t *testing.T) {
workdir := t.TempDir()
pluginDir := t.TempDir()

writeExistingSite(t, workdir, "hasura.conf", "api.staging.nself.org")
writePluginNginxRoute(t, pluginDir, "gateway", "route.conf", "gateway.staging.nself.org")
writeExistingSite(t, workdir, "hasura.conf", "api.stack-two.nself.org")
writePluginNginxRoute(t, pluginDir, "gateway", "route.conf", "gateway.stack-two.nself.org")

cfg := minimalTestConfig("staging.nself.org")
cfg := minimalTestConfig("stack-two.nself.org")

count, err := InjectPluginNginxRoutes(workdir, pluginDir, cfg)
if err != nil {
Expand All @@ -126,8 +126,8 @@ func TestInjectPluginNginxRoutes_SelfUpdateNotAConflict(t *testing.T) {
workdir := t.TempDir()
pluginDir := t.TempDir()

writePluginNginxRoute(t, pluginDir, "gateway", "route.conf", "gateway.staging.nself.org")
cfg := minimalTestConfig("staging.nself.org")
writePluginNginxRoute(t, pluginDir, "gateway", "route.conf", "gateway.stack-two.nself.org")
cfg := minimalTestConfig("stack-two.nself.org")

if _, err := InjectPluginNginxRoutes(workdir, pluginDir, cfg); err != nil {
t.Fatalf("first inject: unexpected error: %v", err)
Expand Down Expand Up @@ -159,14 +159,14 @@ func writeSite(t *testing.T, sitesDir, name, body string) {
}

// TestCheckServerNameUniqueness_ReportsDuplicate reproduces the staging
// finding: two generated site files both claim api.staging.nself.org.
// finding: two generated site files both claim api.stack-two.nself.org.
// `nginx -t` calls that "syntax is ok" and merely logs that it ignored one,
// so post-build validation has to be the thing that fails the build — and
// it has to name BOTH files, or the operator cannot tell which route died.
func TestCheckServerNameUniqueness_ReportsDuplicate(t *testing.T) {
sitesDir := filepath.Join(t.TempDir(), "sites")
writeSite(t, sitesDir, "hasura.conf", "server {\n server_name api.staging.nself.org;\n}\n")
writeSite(t, sitesDir, "ir-gateway.conf", "server {\n server_name api.staging.nself.org;\n}\n")
writeSite(t, sitesDir, "hasura.conf", "server {\n server_name api.stack-two.nself.org;\n}\n")
writeSite(t, sitesDir, "ir-gateway.conf", "server {\n server_name api.stack-two.nself.org;\n}\n")

result := PostValidateResult{NginxValid: true}
checkServerNameUniqueness(sitesDir, &result)
Expand All @@ -177,7 +177,7 @@ func TestCheckServerNameUniqueness_ReportsDuplicate(t *testing.T) {
if result.NginxValid {
t.Error("NginxValid stayed true despite a duplicate server_name — the build would not fail")
}
for _, want := range []string{"api.staging.nself.org", "hasura.conf", "ir-gateway.conf"} {
for _, want := range []string{"api.stack-two.nself.org", "hasura.conf", "ir-gateway.conf"} {
if !strings.Contains(result.Errors[0], want) {
t.Errorf("error %q does not name %q", result.Errors[0], want)
}
Expand Down Expand Up @@ -297,14 +297,14 @@ func TestPostValidate_FailsOnDuplicateServerName(t *testing.T) {
sitesDir := filepath.Join(dir, "nginx", "sites")
// Two generated site files claiming one FQDN on one port: the core
// Hasura route and an api-docs route that landed on the same name.
writeSite(t, sitesDir, "hasura.conf", "server {\n listen 443 ssl;\n server_name api.staging.nself.org;\n}\n")
writeSite(t, sitesDir, "api-docs.conf", "server {\n listen 443 ssl;\n server_name api.staging.nself.org;\n}\n")
writeSite(t, sitesDir, "hasura.conf", "server {\n listen 443 ssl;\n server_name api.stack-two.nself.org;\n}\n")
writeSite(t, sitesDir, "api-docs.conf", "server {\n listen 443 ssl;\n server_name api.stack-two.nself.org;\n}\n")

result := PostValidate(composePath, sitesDir)

found := false
for _, e := range result.Errors {
if strings.Contains(e, "api.staging.nself.org") {
if strings.Contains(e, "api.stack-two.nself.org") {
found = true
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/build/postvalidate_nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
// `conflicting server name "x" on 0.0.0.0:443, ignored`, keeps whichever
// block it loaded last, and still prints "syntax is ok" — so
// checkNginxSyntax below passes and one of the two routes is silently
// dead. That is how api.staging.nself.org ended up served by an
// dead. That is how api.stack-two.nself.org ended up served by an
// unintended block on 2026-09-03.
//
// Three separate writers populate nginx/sites/ in one build — the nginx
Expand Down
4 changes: 2 additions & 2 deletions internal/compose/fronted_stack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package compose
// incident: `nself start` in /opt/nself-ntask failed with "port 80/443 held
// by docker-proxy" because the generated docker-compose.yml always defines
// an nginx service, even for a stack whose domains are actually served by
// ANOTHER stack's nginx (nself-web fronted task.staging.nself.org). That
// ANOTHER stack's nginx (nself-web fronted task.stack-two.nself.org). That
// nginx container can never bind 80/443 — the fronting stack's nginx
// already holds them — so it sits forever as one unhealthy container
// `nself status` can never clear (reported "6/7").
Expand All @@ -28,7 +28,7 @@ func frontedTestConfig(t *testing.T) *config.Config {
t.Helper()
cfg := &config.Config{
ProjectName: "ntask",
BaseDomain: "task.staging.nself.org",
BaseDomain: "task.stack-two.nself.org",
Env: "dev",
DockerNetwork: "ntask_network",
Postgres: config.PostgresConfig{
Expand Down
2 changes: 1 addition & 1 deletion internal/config/types_datastores.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ type NginxConfig struct {
// wire this project's containers onto the fronting stack's Docker
// network — the fronting nginx's `resolver 127.0.0.11` can only reach
// container names on a network it is attached to, so making
// auth.task.staging.nself.org resolve ntask_auth still requires the
// auth.task.stack-two.nself.org resolve ntask_auth still requires the
// operator to attach the two projects' networks (or declare one as
// external in both compose files) by hand. That is a bigger, riskier
// change — automatically rewriting a Docker network topology from a
Expand Down
2 changes: 1 addition & 1 deletion internal/doctor/hardening_check_nginx_zones.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func extractNginxServerBlocks(content string) []nginxServerBlock {
}

// nginxHostnameFirstLabel returns the lowercased first dot-separated label
// of a server_name hostname, e.g. "auth.myapp.staging.nself.org" -> "auth".
// of a server_name hostname, e.g. "auth.myapp.stack-two.nself.org" -> "auth".
func nginxHostnameFirstLabel(hostname string) string {
if i := strings.IndexByte(hostname, '.'); i >= 0 {
return hostname[:i]
Expand Down
10 changes: 5 additions & 5 deletions internal/doctor/hardening_check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ func TestCheckHardeningNginxRateZones_ServiceIdentity(t *testing.T) {
"auth.conf": `
server {
listen 443 ssl;
server_name auth.staging.nself.org;
server_name auth.stack-two.nself.org;
location / {
limit_req zone=auth burst=5 nodelay;
set $up_auth auth:4000;
Expand All @@ -323,7 +323,7 @@ server {
"hasura.conf": `
server {
listen 443 ssl;
server_name api.staging.nself.org;
server_name api.stack-two.nself.org;
location / {
limit_req zone=graphql_api burst=20 nodelay;
set $up_hasura hasura:8080;
Expand Down Expand Up @@ -366,7 +366,7 @@ location /api/ { limit_req zone=api; }
"static.conf": `
server {
listen 443 ssl;
server_name static.staging.nself.org;
server_name static.stack-two.nself.org;
location / {
limit_req zone=static burst=10 nodelay;
set $up_static frontend:3000;
Expand All @@ -384,7 +384,7 @@ server {
files: map[string]string{
"auth.conf": `
server {
server_name auth.staging.nself.org;
server_name auth.stack-two.nself.org;
location / {
limit_req zone=auth burst=5 nodelay;
proxy_pass http://auth:4000;
Expand All @@ -393,7 +393,7 @@ server {
`,
"hasura.conf": `
server {
server_name api.staging.nself.org;
server_name api.stack-two.nself.org;
location / {
proxy_pass http://hasura:8080;
}
Expand Down
8 changes: 4 additions & 4 deletions internal/nginx/dynamic_resolve_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package nginx

// dynamic_resolve_test.go — regression coverage for the 2026-09-03 staging
// outage (auth.task.staging.nself.org 502 after ntask_auth was recreated).
// outage (auth.task.stack-two.nself.org 502 after ntask_auth was recreated).
//
// Purpose: prove every generated service route resolves its proxy target at
// request time (via the Docker embedded DNS resolver) instead of caching a
Expand Down Expand Up @@ -34,9 +34,9 @@ var staticUpstreamBlock = regexp.MustCompile(`(?m)^\s*upstream\s+\S+\s*\{`)
// the worker process, so a container recreate (nself restart, OOM, `docker
// system prune`, a plain rebuild) 502s every request through that route
// until someone notices and runs `nginx -s reload` by hand — exactly what
// happened to auth.task.staging.nself.org on 2026-09-03.
// happened to auth.task.stack-two.nself.org on 2026-09-03.
func TestServiceRoutesResolveUpstreamDynamically(t *testing.T) {
cfg := &config.Config{BaseDomain: "staging.nself.org"}
cfg := &config.Config{BaseDomain: "stack-two.nself.org"}
cfg, err := config.ApplyDefaults(cfg)
if err != nil {
t.Fatalf("ApplyDefaults() error: %v", err)
Expand Down Expand Up @@ -117,7 +117,7 @@ var proxyVarAssignment = regexp.MustCompile(`(?m)^\s*set\s+\$\S+\s+(\S+);`)
// then fails every request against the host "http" — the same
// silent-until-traffic failure class as the stale-IP bug this file covers.
func TestProxyTargetsHaveExactlyOneScheme(t *testing.T) {
cfg := &config.Config{BaseDomain: "staging.nself.org"}
cfg := &config.Config{BaseDomain: "stack-two.nself.org"}
cfg, err := config.ApplyDefaults(cfg)
if err != nil {
t.Fatalf("ApplyDefaults() error: %v", err)
Expand Down
4 changes: 2 additions & 2 deletions internal/nginx/routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,14 @@ func TestCoreRoutes_NoAppName_BareScheme(t *testing.T) {
// produces app-prefixed routes ("api.task", "auth.task") so the final
// server_name is "api.task.{BASE_DOMAIN}" instead of "api.{BASE_DOMAIN}".
func TestCoreRoutes_AppName_PrefixesSubdomains(t *testing.T) {
cfg := &config.Config{BaseDomain: "staging.nself.org", AppName: "task"}
cfg := &config.Config{BaseDomain: "stack-two.nself.org", AppName: "task"}
cfg, err := config.ApplyDefaults(cfg)
if err != nil {
t.Fatalf("ApplyDefaults() error: %v", err)
}

g := &Generator{cfg: cfg}
entries := g.coreRoutes(cfg.BaseDomain, "staging-nself-org")
entries := g.coreRoutes(cfg.BaseDomain, "stack-two-nself-org")

routesByFile := make(map[string]string)
for _, e := range entries {
Expand Down
Loading