Skip to main content

Isolated test instance

The test instance runs public lifecycle scripts from its own physical source root. Its mounts, ownership labels, initializers, configuration, and runtime state belong to that copy. A shell override of ECS_INSTANCE_ID does not safely select it.

Prepare the routing and prerequisites in test installation first.

Fixed test bundle commands​

Run from the original project root:

bash testInstance.sh prepare
bash testInstance.sh status
bash testInstance.sh start
bash testInstance.sh stop
bash testInstance.sh quick-start
bash testInstance.sh restart

prepare creates a source snapshot and configuration without starting services. It refuses automatic replacement of an existing or partial snapshot. New or modified source needs file-specific --include-source RELATIVE_FILE approval during preparation. Path and secret filters still apply. Later product edits do not automatically enter the snapshot.

Lifecycle commands check provenance, configuration, Docker ownership, and isolation boundaries. Startup also needs host memory and exclusivity checks. A separate project ID does not supply separate RAM.

Attach to running test terminals​

From the original project root, attach to the existing running test terminals:

bash testServerAttach.sh

To attach only to the BFF service, use:

bash testServerAttach.sh bff

The helper discovers the clone root from running BFF containers under .agenttemp/ or .docker-state/. Exactly one root must qualify with ECS_TEST_INSTANCE=true or ECS_PUBLIC_HOST_PREFIX=test. With zero or multiple matches, use bash devServerAttach.sh from the intended runtime's own root.

These commands do not start or restart services. To detach from tmux, press Ctrl+b, then d. For direct Docker attachment, press Ctrl+p, then Ctrl+q.

Deployment-derived public origins​

Public hostnames can use the source deployment name while the test keeps its own runtime identity. New test preparation captures ECS_ORIGIN_DEPLOYMENT_ID from the source, or its ECS_INSTANCE_ID when absent. It then sets ECS_PUBLIC_HOST_PREFIX=test before assigning the isolated runtime settings.

These two optional selectors control the public names:

VariableConsumerPurposeType or formatRequired whenDefaultExampleConstraints or security
ECS_ORIGIN_DEPLOYMENT_IDRuntime setup, Compose resolution, proxy plannerSelect the deployment label in public hostsLowercase deployment label, 1–40 charactersOptionalECS_INSTANCE_IDttrmbStarts with a letter or digit. Remaining characters are lowercase letters, digits, or hyphens. Does not change runtime ownership.
ECS_PUBLIC_HOST_PREFIXRuntime setup, Compose resolution, proxy plannerAdd one label before each public service nameEmpty or one lowercase DNS label, at most 63 charactersOptionalEmptytestUse bare test, without a trailing dot. A nonempty label starts and ends with a letter or digit.

The URL pattern is https://[prefix.]service.<origin-deployment>.<suffix>:<port>. The existing DEV_DOMAIN_SUFFIX and NPM_HTTPS_PORT supply the suffix and port. Their defaults are ecas.local and 8443.

For source deployment ttrmb, prefix test, and those defaults, the eight derived settings are:

Derived variableValue
MAILGEN_PUBLIC_URLhttps://test.bff.ttrmb.ecas.local:8443
BETTER_AUTH_URLhttps://test.account.ttrmb.ecas.local:8443
NEXT_PUBLIC_BETTER_AUTH_URLhttps://test.account.ttrmb.ecas.local:8443
ACCOUNT_PUBLIC_URLhttps://test.account.ttrmb.ecas.local:8443
NEXT_PUBLIC_ACCOUNT_URLhttps://test.account.ttrmb.ecas.local:8443
FORMGEN_BETTERAUTH_URLhttps://test.formgen.ttrmb.ecas.local:8443
DOCUSAURUS_PUBLIC_URLhttps://test.docs.ttrmb.ecas.local:8443
DOCUSAURUS_MCP_PUBLIC_URLhttps://test.docs.ttrmb.ecas.local:8443/mcp/

Either nonempty selector enables derivation of all eight values during ordinary runtime configuration resolution. Do not configure eight independent URL overrides for this mode. With both selectors empty, ordinary runtime reads preserve existing explicit BETTER_AUTH_URL, NEXT_PUBLIC_BETTER_AUTH_URL, and FORMGEN_BETTERAUTH_URL values. Setup recalculation still derives all eight URLs.

The selectors do not rename containers, upstream aliases, volumes, networks, database identities, or remote namespaces. For runtime ecas-test, the example BFF route still targets ecas-test-bff:5173. The Account route separately targets ecas-test-account:5573.

New preparation records both calculated browser origins in the manifest. Lifecycle admission requires routing approval and an exact match with those stored origins. Removing the selectors does not remove that check. Historical selector-free manifests without stored origins retain their earlier admission contract.

Keep an existing frozen bundle unchanged. Root source edits do not update its configuration, copied helpers, or prior evidence. Prepare an explicitly approved new snapshot for the new origin behavior. Do not rewrite historical manifests or approvals.

Authentication isolation​

Shared authentication requires paired HTTPS BFF and Account hosts with the same deployment suffix, prefix, and port. For the example pair, the shared cookie domain is .ttrmb.ecas.local. The test cookie prefix is ecas-shared-test. The unprefixed deployment retains ecas-shared.

The test BFF OAuth state cookie is ecas_test_oauth_state. Account uses ecas_account_test_oauth_state. Unprefixed deployments retain ecas_oauth_state and ecas_account_oauth_state. These names separate test sessions and OAuth state from the production names.

Sibling hosts still share a parent domain. Cookie names do not create a security boundary against a hostile sibling host. Keep separate test databases, auth secrets, and controlled proxy routes.

Standalone Account development preserves host-only, nonsecure cookies only when both configured application origins use HTTP and exact localhost, without user information. Mixed public/localhost origins and other loopback spellings do not receive this exception.

See certificate trust and Microsoft callbacks before browser login. A fresh Microsoft login also needs configured BFF Microsoft credentials and a granted, nonexpired tenant connection, even with Graph workers off. OAuth settings and redirect registration alone are insufficient. See fresh Microsoft login prerequisites for operator preparation and the separate backend Graph credentials.

The normal seed does not create the first tenant connection in a fresh isolated database. Complete legitimate operator setup before Microsoft app login. After login, link the mailbox separately and complete personal key generation, backup confirmation, and activation before onboarding. Use a trusted HTTPS origin for browser key setup. See the linked prerequisites for the existing-owner recovery path and matching onboarder/WebhookCatcher client state. Keep production storage credentials, encryption keys, sessions, and data isolated.

Correct configuration does not establish a successful login, logout isolation, or an OAuth roundtrip.

Data and resources​

The generated test configuration uses fresh local storage, auth/encryption material, and isolated Kafka/Qdrant namespaces. Its selected placement is real partialRemote; provider/ingress flags are disabled by the test policy. It does not copy production mailbox data.

The fixed wrapper remains personal-only with Microsoft disabled and a random ECAS_DEPLOYMENT_TENANT_ID authorization namespace. A separately prepared, operator-enabled Microsoft variant requires the intended, verified app-only Entra tenant before its first authorization bootstrap. Set ECAS_DEPLOYMENT_TENANT_ID equal to that BACKEND_TENANT_ID. Keep independent databases, auth/encryption keys, stores, remote namespaces, and cookies. Tenant identity alone does not isolate storage.

See Microsoft tenant selection and existing-state limits before preparation or migration. An initialized test cannot change tenants through an .env switch alone. Fresh Microsoft onboarding across different deployment tenants is currently unsupported by the legacy admission path.

The stock generated test default remains ECS_MEMORY_GIB=30, bounded, with a 4 GiB swap budget. It does not inherit a larger production profile. The startup minimum guard is authoritative for the selected services.

A separately prepared, operator-enabled Microsoft test with the observed 33 selected services now needs a larger existing profile. The onboarder minimum is 2048 MiB, and the full-development independent queue minimum is 768 MiB. This selection needs 32,192 MiB at the 30 GiB profile minimums, which exceeds that profile. At the existing 35+ GiB profile minimums, it needs 38,592 MiB. The selected 40 GiB profile fits those minimums. This selection is not a universal default, guaranteed free RAM, or evidence of successful startup or mailbox completion.

Before a normal restart, keep only one full stack active and check host RAM and swap admission. Preserve databases, object stores, volumes, and keys. Apply an approved profile change through the normal restart from the selected physical runtime root. Do not reset data. A service cgroup out-of-memory (OOM) failure does not establish host OOM. Check host memory and kernel OOM evidence before stopping the full stack.

The current Graph FTS export processes one item per chunk, which reduces batch buffering. A single large item still needs an adequate service memory budget. Record successful startup and mailbox completion only from actual execution evidence. Selecting 40 GiB alone proves neither outcome.

Test image-build targets run individually. Normal initialization runs one Compose build command per image target for every runtime, regardless of the test flag. A failed build is not readiness. Correct the reported prerequisite before resuming.

Proxy boundaries​

The test proxy path cannot start or restart the shared proxy. Use an existing proxy and distinct hosts/callbacks/certificates. Name resolution must work on both the browser machine and server/WSL host. Adding approved Nginx routes can require a configuration reload without restarting the container.

Certificate creation, hostname coverage, and browser trust are separate checks. SANs determine hostname coverage independently of the displayed certificate CN. A T3 preview can run in Linux/WSL while its interface runs on Windows. Windows Root trust does not cover that preview.

Use the browser certificate trust procedure for the actual browser environment. It covers served-certificate fingerprints, Windows approval, Chromium NSS database selection, exact leaf trust, and a scoped browser restart. Linux system trust alone does not establish Chromium NSS acceptance. Check both selected hosts with HTTPS certificate checks enabled before login.

Keep original PostgreSQL/S3 and other backup-test stores outside this test instance. Stop preserves test data too. This guide does not authorize a reset or volume deletion.

Separately prepared source snapshots​

A source acceptance check can use a separately prepared physical snapshot with a fresh runtime identity and private configuration. This is an operator-managed setup. testInstance.sh does not select a custom identity. Preserve any existing fixed ecas-test bundle.

Before startup, review storage, encryption keys, ownership labels, networks, and public routing. Keep source storage and keys unchanged. Preserve the shared proxy and unrelated monitoring services.

Run ordinary lifecycle scripts from the selected physical runtime root. Replace /path/to/prepared-runtime with that reviewed directory:

cd /path/to/prepared-runtime
bash startDevServers.sh
bash stopDevServer.sh
bash quickStarter.sh
bash restartDevServers.sh

These are separate lifecycle actions. quickStarter.sh resumes existing containers. It does not apply a new configuration or prepare missing containers. Do not use reset for this workflow.

Check snapshot source provenance​

Commit the implementation in each affected repository before preparation. Preparation selects immutable commits for the root repository and selected subrepositories. It copies committed files, plus approved file-specific overlays and the required snapshot helpers. Uncommitted application edits do not enter the snapshot automatically.

Before interpreting build or browser evidence, check the selected repository revisions and file hashes in the source manifest. Check that they cover the intended implementation. Also check the actual resolved ECS_APPLICATION_SOURCE_ROOT and the application mounts or build contexts that consume it.

ECS_APPLICATION_SOURCE_ROOT selects application code separately from the lifecycle root. Its default is .. A reviewed absolute source root can redirect code while configuration, runtime identity, state, and data remain clone-local. See the canonical environment reference for the wider configuration contract.

Root edits do not refresh an old prepared bundle or its copied lifecycle helpers. If current source is required, prepare a new physical snapshot from the intended commits. Preserve the old snapshot, its pinned manifest, and its historical evidence. A restart does not replace snapshot preparation. Source checks alone do not prove successful startup or a browser journey.

Switch separately prepared placement variants​

An operator can prepare separate private FullLocal and PartialRemote environment variants for the same reviewed runtime. The active root .env selects placement. FullLocal uses ECS_DEPLOYMENT_MODE=fulllocal and ECS_REMOTE_SIMULATION=false. Real PartialRemote uses ECS_DEPLOYMENT_MODE=partialRemote and ECS_REMOTE_SIMULATION=false.

Preserve local PostgreSQL, S3, auth/encryption keys, and volumes across both variants. Stop the actual selected runtime with its current configuration before replacing the active .env. Then use ordinary start or restart to apply the selected configuration. quickStarter.sh only resumes existing containers and cannot apply the switch. Do not reset or delete volumes.

In the real PartialRemote variant, KAFKA_CLUSTER_ID must equal REMOTE_KAFKA_EXPECTED_CLUSTER_ID for the actual selected, owned remote cluster. The FullLocal variant retains its own local cluster ID and broker data. Unique Kafka topics and consumer groups isolate application namespaces. They do not replace cluster identity. A unique Qdrant collection is another separate namespace.

The remote KNOWLEDGE_QDRANT_COLLECTION must already exist with vector size 1536 and distance Cosine. Normal Qdrant dependency preflight only GETs the collection. It fails on HTTP 404 in real remote mode and cannot create it. An operator can provision a newly owned, absent collection through the normal Qdrant admin API. Never repoint old data or reuse another deployment's collection name.

See remote deployment placement and state transfer before switching placement. Exact public-origin and route ownership remain required for Office. Successful remote dependency probes do not prove a complete hybrid application journey or an Office callback.

Production builds need the runtime DATABASE_URL and NODE_ENV=production. Ordinary container startup with bash startDevServers.sh --prod supplies the production environment through Compose. A bare host build without DATABASE_URL can fail during module import. A host build with NODE_ENV=development can fail during prerendering. Use the prepared runtime configuration for production builds. A successful build does not establish authenticated browser or remote-service acceptance.

Standard cold startup mounts the canonical database SQL read-only at /runtime/database-sql. The database initializer installs the actor-rights and policy SQL, then runs the normal seed. It runs the actual authorization migrations only when betterauth.user and authorization.subjects are both globally empty and the deployment tenant has no migration marker. Existing databases still require the explicit authorization migration workflow. Do not bypass the authorization gate or insert a migration marker without the migrations.

Development HMR accepts the hostname from MAILGEN_PUBLIC_URL alongside the existing Account auth hostnames in allowedDevOrigins. This permits the configured BFF origin without a wildcard. Keep public browser and auth URLs aligned with the proxy routes. Restart the BFF development service after changes to next.config.js.

MAILGEN_PUBLIC_URL also adds its exact origin (scheme, hostname, and port) to BFF authenticated realtime and cookie sync. HTTP origin checks also include it for Complexity mutations, approvals, composer draft attachments, admin proxy mutations, and search mutations. These checks use the URL origin, while HMR uses only the hostname. Configure the specific public URL, without a wildcard. Session and permission checks still apply.