SOP: detect and remediate cross-connected Orbitype projects
Detect shared API keys or databases across Nuxt and Orbitype projects, stop unsafe writes, migrate clean data and prevent connector mix-ups.
Cross-connected Orbitype projects occur when two Nuxt applications reuse one API key, separate connectors point to the same database, or a credential labeled for one project actually opens another project’s content. The safe response is to freeze writes, verify the real SQL target with read-only checks, back up the data, create an isolated connector and migrate only records with confirmed ownership.
This SOP is based on an anonymized production incident. The clients are called Project A and Project B; domains, database IDs, key fragments, dates, row counts and legal content have been removed. Every command uses placeholders and contains no real credential.
The short answer
Project A → key A → connector A → database A
Project B → key B → connector B → database B
Before any write:
key fingerprint + current_database() + current_schema() + expected slugs
Repository names, Vercel project names and labels in a CMS dashboard are not isolation evidence. The useful evidence is the identity returned by the SQL session and whether the stored content belongs to that application. PostgreSQL documents that current_database() returns the current database and current_schema() returns the first schema in the search path.
What failed in the anonymized incident
Two failures reinforced each other:
- Project B’s local environment contained a credential associated with Project A.
- Project B’s original connector no longer resolved to a usable database.
- The reachable database contained records from both products.
- Both live sites looked plausible on their most visited routes because branding, navigation and components lived in each frontend.
- An SEO crawl found unexpected 404s and inconsistent routes before production showed an obvious homepage failure.
A correction based only on the home slug then wrote to the other project’s content. The rendering error was merely a symptom; the incident was a loss of data isolation.
Signals that must stop writes
| Signal | Possible meaning | Response |
|---|---|---|
| Another project’s copy appears in warnings or SSR | Foreign payload or incompatible schema | Freeze writes |
| Object props arrive as plain strings | Sections were authored for another frontend | Verify database and slugs |
| Canonical routes return unexpected 404s | CMS inventory is incomplete or mixed | Compare sitemap and slugs |
| Two environments produce the same key fingerprint | Credential reuse | Rotate and audit |
| Different keys return the same database | Connectors lack isolation | Split the destination |
| connector not found plus an empty dashboard field | Broken or deleted connector | Provision a clean target |
| The live homepage looks fine | Only visited routes were tested | Do not authorize writes |
1. Contain the incident first
Disable seeds, schema installers, admin editors and one-off scripts. Do not run INSERT, UPDATE or DELETE until you know the current database and the owner of the affected records.
If an incorrect write already happened:
- stop new deployments and automation;
- preserve logs without copying secrets;
- restore a validated backup if production is affected;
- smoke-test core, sitemap and legal routes;
- rotate exposed credentials after the service is stable.
Making a translation helper tolerate unexpected strings may prevent an SSR crash, but it does not fix the wrong database target. Treat that change as rendering containment, not data remediation.
2. Identify keys without publishing them
Never paste API keys into chats, tickets, screenshots or runbooks. Instead of saving the value or its suffix, calculate a short SHA-256 fingerprint locally:
printf '%s' "$ORBITYPE_API_SQL_KEY" | shasum -a 256 | cut -c1-12
The fingerprint can reveal reuse without exposing part of the secret. Maintain a private registry containing:
| Field | Safe example |
|---|---|
| Project | Project A |
| Repository | repo-a |
| Key fingerprint | 12 SHA-256 characters |
| Database identity | stored in a secret manager or private runbook |
| Environment | Development, Preview or Production |
| Status | verified, migrating or retired |
3. Run read-only identity checks
Use local environment variables and placeholders. Do not write the key value into the command:
curl --fail-with-body --silent --show-error "$ORBITYPE_API_SQL_URL" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $ORBITYPE_API_SQL_KEY" \
--data '{"sql":"SELECT current_database() AS db, current_schema() AS schema"}'
Then inspect the inventory and a small content sample:
SELECT slug FROM pages ORDER BY slug LIMIT 100;
SELECT
slug,
left(title::text, 120) AS title_sample,
json_array_length(sections) AS section_count
FROM pages
ORDER BY updated_at DESC
LIMIT 20;
Titles are not enough. Compare component names and prop shapes too. If Project A expects localized objects but receives flat strings or section types missing from its Vue catalog, it is probably reading foreign data.
Diagnosis matrix
| Result | Likely diagnosis |
|---|---|
| Same fingerprint and same database | A key was copied between projects |
| Different fingerprints and same database | Separate connectors share one destination |
| Key A opens Project B slugs | Miswired connector or misleading label |
| Key B returns connector not found | Revoked credential or broken connector |
| Distinct databases and coherent content | Isolation looks correct; still verify ownership |
4. Why production can look healthy
Each deployment ships its own layout, navigation, styles and component catalog. The CMS responds only to the requested slug. A compatible homepage may therefore render while less-visited pages return 404, use the wrong meaning for a slug or contain sections the frontend cannot render.
Local, Preview and Production can also use different values. Vercel states that environment-variable changes apply only to new deployments, so a healthy live site does not prove that a local file reaches the same database.
Useful passive checks include:
- crawling canonical routes after every go-live;
- comparing the sitemap, an expected-route allowlist and the CMS inventory;
- alerting on slugs from an unrelated product;
- verifying database identity before every write-capable job.
5. Back up and classify before migration
Create a complete, verifiable backup. With direct PostgreSQL access, use the backup method approved by your provider; PostgreSQL documents regular backups plus pg_dump and pg_restore. If the SQL API is the only access path, export affected tables to encrypted storage outside the repository.
Classify every row:
- approved: clearly owned by the destination project;
- excluded: clearly owned by the sibling project;
- review required: ownership is uncertain;
- legal or sensitive: requires explicit human review.
Prefer an allowlist of expected slugs over “copy everything except these pages.” A denylist can silently reintroduce another client’s legal pages, SEO metadata or settings.
6. Provision a clean target and migrate
If the connector cannot be repaired:
- create a new Orbitype project or connector;
- confirm that the dashboard shows a valid database;
- generate a new credential;
- run current_database() and current_schema();
- confirm that pages is missing or empty;
- install the versioned schema;
- import approved records only;
- verify counts, slugs, the homepage and component types.
If an INSERT returns an HTTP error, query for the row before retrying. A failed response does not always prove that the transaction was rolled back; blind retries can produce duplicate or constraint errors.
7. Cut over without losing rollback
Update Development, Preview and Production deliberately. Nuxt keeps credentials out of the browser when they are declared in private server runtime configuration; see the official useRuntimeConfig guidance.
For Vercel:
- add the new key to the intended environment;
- create a new deployment;
- smoke-test home, business, sitemap and legal routes;
- confirm that reads reach the new database;
- keep the old destination temporarily available for rollback;
- revoke the old key after every environment is verified.
Vercel’s secret rotation guide likewise recommends keeping the old credential active until the new deployments have been tested.
8. Add a gate before every seed
The strongest control is to reject writes by default:
async function assertSafeCmsTarget(options: {
expectedDatabase: string
allowNonEmpty: boolean
}) {
const identity = await sql(
"SELECT current_database() AS db, current_schema() AS schema",
)
if (identity[0].db !== options.expectedDatabase) {
throw new Error("CMS target does not match the expected project")
}
const result = await sql("SELECT count(*)::int AS n FROM pages")
if (result[0].n > 0 && !options.allowNonEmpty) {
throw new Error("CMS target is not empty; seeding requires review")
}
}
For a non-empty target, require an interactive confirmation and display sample slugs. Store the expected identity in protected configuration rather than hardcoding a production identifier in a public repository.
Pre-write checklist
- Key fingerprint compared with sibling projects.
- current_database() and current_schema() verified.
- Slugs and section shapes match the repository.
- Complete backup stored outside the repository.
- Legal records reviewed by a person.
- New target is empty or explicitly approved.
- Rollback plan is documented.
- Vercel was redeployed after changing variables.
- Old key is revoked only after smoke testing.
Frequently asked questions
How can I tell whether two Orbitype projects share a database?
Compare one-way key fingerprints and run current_database() with every credential. Different keys can still reach the same database, so inspect slugs, titles and component types as well.
What should I do before correcting content?
Freeze writes, identify the real target, create a backup and classify record ownership. Do not use an UPDATE against the home slug as a diagnostic test.
Will rotating the key repair a broken connector?
Not necessarily. If the connector does not resolve to a valid database, create a clean target, migrate approved data, cut environments over and rotate credentials afterward.
How do I prevent a seed from affecting another project?
Run a blocking gate that validates identity, table state and sample slugs. An installer or seed must never write merely because it found a key in the environment.
The same server-only secret and layered validation principles apply to form integrations; see the related Orbitype, Vercel and SendGrid SOP.