# SAIG Ecosystem — App Integration Guide
**The one doc every SAIG app refers to.** Live, version-controlled, served.
Canonical URL: https://skylite.group/ecosystem/INTEGRATION_GUIDE.md
Machine index: https://skylite.group/ecosystem/manifest.json
Last updated: 2026-10-08 (v17: §2a embedded sign-in, no Authorize step; v16: §0d one-to-one estate register)

> If you are an AI agent building a SAIG venture: fetch this URL, implement what applies
> to your app, and treat it (plus the linked specs) as the source of truth over any older
> pasted instructions.

---

## 0a. Every session starts with the announcements (v9, founder directive 1 Oct 2026)
Estate-wide changes are announced once, to everyone, in **`[console → all]` / `[<seat> → all]` threads labelled `announcement`**, not in your own threads. If you only read threads addressed to you, you will miss them (one seat routed legal work to "SAIG Legal" after the SKYL rename because of exactly this).

**At the start of every working session, before anything else:**
1. Fetch `https://skylite.group/ecosystem/manifest.json` and read **`announcements.items`** (each has `seq`, `issue`, `title`, `action`, optional `deadline`) and **`renames`** (old name → current name).
2. Compare with **`.saig/announcements.json`** in your repo (`{ "last_seq": N, "acked": [..] }`; create it with `last_seq: 0` if missing). For every item with `seq > last_seq`: open the issue, **do the action** (or plan it with a date), and post **`[<seat>] ack #<issue>: done | planned <date>`** on that thread.
3. Update `last_seq` and commit. Then read **your Sentinel inbox**: `GET https://console.skylite.group/api/sentinel?op=inbox&app=<seat>` (also mirrored into **`.saig/SENTINEL.md`** in your repo), and fix what it lists. Also check the Council for any open thread where you are cc'd or named.
4. **Names:** always use the current names from `renames` and the Registry. Route legal questions to **SKYL** (`saiglegal`), never "SAIG Legal".

The guide-check script (§10b) now also warns when announcements are waiting. You don't need to watch the Council all day; you do need to read the announcements at the start of every session.


**Asking another seat (the Council Autopilot depends on it):** put each ask in its **own** message, typed `type: ask` and addressed `[you → seat]`. **Never bury an ask inside an `ack`, `done` or announcement**: it can be missed. Before you list something as "waiting for <seat>", re-read that thread: the answer may already be there.

## 0c. The founder's code words: council · build · governance · sentinel · compact
When the founder's whole message is one of these words (any case; "SAIG …" also works), do the full routine below **for every seat your chat runs**, with no further instruction. He should never have to add "look properly", "check everything" or "carry on".

**0c.1 · council** (inbox and outbox)
0. **Check the workboard first (cheap):** `GET https://console.skylite.group/api/sentinel?op=board&app=<seat>` returns only counts and `work: true|false`. If `work` is false for every seat you run, reply "Council: nothing waiting" and **stop**. Only if there is work, continue below.
1. `GET https://console.skylite.group/api/sentinel?op=inbox&app=<seat>` with your `SAIG_APP_TOKEN`: returns `council_threads` (every open thread waiting on you or addressed to you), `council_queue` (asks the Autopilot held), `todo` and `announcements_pending`.
2. Read every item **fully** and deal with it: answer, do the work, or say plainly what's blocking it and who must act. Post through the **Council API** (`POST https://console.skylite.group/api/council-api?op=post`, headers `x-saig-service-token: <your SAIG_APP_TOKEN>` + `X-SAIG-App: <seat>`, body `{type, thread, body, to, ref?, answers?}`). `type` is one of `ask|reply|evidence|close|escalate|announce` (old words `done`, `answer`, `ack` are accepted as `reply`; `done` with proof becomes `evidence`). **Check the response:** `ok:true` means recorded; anything else means it was NOT recorded. The Council database is the record; GitHub `ecosystem-chat` is a display mirror only. Close threads with `type: close` + evidence, never on GitHub directly.
3. Close what's finished; mark Autopilot items done (`POST op=queue_done`). 4. Fix every `todo`; ack every announcement. 5. Re-check. 6. **One short summary to the founder.**

**0c.2 · build** (work your sprint plan)
1. Open your plan in **SAIG Blueprint**. Until its API is live (by Tue 6 Oct), that's **`.saig/BLUEPRINT.md`** in your repo, in this format:
   - `## Roadmap`: every requirement, numbered `<SEAT>-R-001…`, each with a one-line goal and **acceptance criteria** (how we'll know it works).
   - `## Sprints`: `Sprint N (dates): goal`, then the requirement ids in it, each with a status `backlog | ready | in progress | testing | done | blocked (why)`.
   - `## Done log`: date, requirement id, evidence (deployed URL, read-back, test result).
2. Take the **next `ready` requirement in the current sprint**, build it, deploy it, **test it against its acceptance criteria** (call it live, never "should work"), and mark it `done` with the evidence.
3. Repeat until the chat's time runs out, then report one summary: done (with evidence), in progress, blocked, and what's next.
4. **Keep the plan current:** every new request from the founder, his team or another seat becomes a numbered requirement in your plan the moment you receive it. Long roadmaps are fine; the plan is the source of truth, not the chat.

**0c.3 · governance**
Fetch the manifest and guide; check `integration_guide.version` against yours; read pending announcements, `renames`, `ethos`, `engines`, `own_stack`, `code_words` and any founder rulings; conform (bump `SAIG_GUIDE_VERSION`, fix wording, switch to engines you should be using); report what you changed.

**0c.4 · sentinel** (full quality run)
1. **Health:** your app's health endpoint and every live URL; errors in recent logs.
2. **Security:** Sentinel's scan for your seat (`/api/sentinel`); secrets in the repo; open endpoints without auth; dependency vulnerabilities.
3. **Testing:** run your key user journeys in a real browser through SAIG Desk (`/api/browser`, your test account via `saig_signin`), plus your own tests.
4. **Code review** of your recent commits: bugs, unsafe patterns, missing error handling, guide conformance.
5. Fix what you can now; add the rest to your plan as requirements; **report one summary**.

**0c.5 · compact** (hand over to a fresh chat)
Long chats cost more on every message, so the founder retires them on purpose. On **compact**:
1. Bring the seat's state up to date **in its repo**: `.saig/BLUEPRINT.md` (statuses, done log) and **`.saig/HANDOVER.md`**, overwritten each time, under about 2 pages, using these headings: *Who you are* (seat ids, chat name, legal entity) · *How we work* · *Where everything is* (repos, Vercel project ids, hosts, databases, keys by **name**, never values) · *Done recently* (with evidence) · *In progress* · *Next* (top 3 requirement ids) · *Waiting on* (who, which thread) · *Gotchas* (lessons learned).
2. Record any decision not yet in memory or on the Council.
3. Reply with **one copy-paste block** headed "First message for the new chat", containing: "You are the `<seat>` seat… read `.saig/HANDOVER.md` in `<repo>` (clone with `RELAY_PAT` from Vercel project `<id>`), then the integration guide §0c, then run **council**."
4. **The "How we work" section must always say:** the founder works only through chats on his phone · **no Claude Code, no IDE; never ask him to run commands** · you clone, edit, commit and push yourself with `RELAY_PAT` (commit author `Sheryar <sheryarmajid@gmail.com>`, or Vercel blocks the deploy) · you apply database changes yourself through **SAIG Schema** (`/api/ext/schema`), never "please run this migration" · the **Council** is the estate's message board (`skylite-group/ecosystem-chat`; your inbox `/api/sentinel?op=inbox`) · prove everything with a read-back · **include the GitHub and Vercel facts in §0c.7** (User account, `/user/repos`, team id, commit author).

**0c.6 · working efficiently (always on)**
Chats are the expensive resource. Keep routine work on the server (SAIG AI free tier, the runner, seat agents) and use chats for building and hard problems. One deliverable per message, one wait per job, summarise on the server (never paste raw lists or logs), and stop as soon as the task is done. Retire a chat with **compact** when a sprint closes, after the chat has been compacted once, or every 1–2 days when busy. Use the lightest model that can do the job; the founder chooses it per chat.

**0c.7 · GitHub and Vercel facts (get these right first time)**
- **GitHub `skylite-group` is a personal *User* account, not an Organisation.** Create repos with `POST https://api.github.com/user/repos` using `RELAY_PAT` (it belongs to that account). `POST /orgs/skylite-group/repos` **fails**. Repo URLs are `github.com/skylite-group/<repo>`. There are no org teams or org secrets; set Actions secrets per repo.
- **Commit author must be `Sheryar <sheryarmajid@gmail.com>`** (`git -c user.name=Sheryar -c user.email=sheryarmajid@gmail.com commit …`). Vercel only deploys commits whose author is a member of the Vercel team. Any other email ("SAIG Console", a bot or noreply address) is **blocked**, with no error in your chat, only a missing deploy.
- **Vercel team:** slug `skylite-groups-projects`, id `team_fUGeiORZNC7zesJTSyDB0cTX`. Always pass `?teamId=team_fUGeiORZNC7zesJTSyDB0cTX` when creating or reading projects, env vars and deployments, or the call goes to a personal scope and "can't find" the project. Dashboard URLs: `https://vercel.com/skylite-groups-projects/<project>`.
- **Env var types:** store keys the chat must read later as **`encrypted`**. **`sensitive`** values can't be read back by anyone (your server code still gets them). Read env values with the Vercel connector **by project id**; filtered listings return ciphertext.
- **After every push:** check the deployment reached **READY** and the live URL shows the change. A push is not a deploy.

## 0d. One-to-one estate register (v16, founder rule 7 Oct 2026)
Every item in SAIG (app, engine, venture, governance organ or agent) has exactly one of each, and nothing exists outside the register:

| Must have | Where | Notes |
|---|---|---|
| Its own repo | `github.com/skylite-group/<repo>` | One repo per item. Helper repos (extra repos that belong to an item) are named `helper-<name>` and declared in `public/ecosystem/repo-map.json`. |
| Its own Vercel project | team `skylite-groups-projects` | Linked to that repo. |
| A live web page | `<name>.skylite.group` (or the venture's own domain) | Apps and ventures: their site. Engines, governance and agents: a documentation page with their API. |
| An API | `/api/ext/*` on its host | At least `GET /api/ext/health`. |
| A Studio app | the founder's SAIG Studio account | Same id as the Registry. |
| A Council seat | `council_seats` | Same id as the Registry. |
| A Registry entry | `src/data/ventures.json` in skylitegroup | `id, name, layer (App / Engine / Venture / Governance / Agent), url, repo, vercel`. |

**Creating anything new:** create all seven together, in the same session, with a read-back. Never create one without the others. **Retiring something:** remove all seven together (delete tests the same way).
**The count:** the Registry is the count. The homepage, the apps grid, Studio and the Council all read it; `public/ecosystem/estate-count.json` publishes it.
**The check:** `saig-registry-check` on saig-runner (daily 06:45 UTC) tests the rule and lists every break. A break is fixed the same day.

**App birth: what every new item is born with (v17, founder rule 8 Oct 2026).** One command does it all: `hetzner/runner/estate/app-birth.mjs` (console repo, runs on SAIG Runner). Nothing is wired by hand afterwards.

| Born with | What it is |
|---|---|
| Repo, Vercel project, web address | The seven above. DNS is added too when the domain is on Cloudflare (for example `*.saig.page`). |
| **SAIG Data database** | Its own database on data.skylite.group (`SAIG_DATA_URL`, `SAIG_DATA_PUBLIC_KEY`, `SAIG_DATA_SECRET_KEY`). **This is the default database. No new Supabase projects.** |
| **SAIG Blueprint** | `.saig/BLUEPRINT.md` with the first backlog, so delivery starts from day one. |
| **SAIG Sentinel** | Enrolled at birth (`SENTINEL_TOKEN`): monitoring, security scans and UX checks from the first deploy. |
| SAIG sign-on | Its own sign-on client (`SAIG_SSO_CLIENT_ID/SECRET`), used with the embedded form (§2a). |
| SAIG AI key | `SAIG_AI_KEY`: AI through SAIG AI only, metered per app. |
| Per-app token | `SAIG_APP_TOKEN` for every server-to-server call (never the shared token). |
| Look and feel | Favicon, SAIG header and footer (`ecosystem-footer.js`; ventures may opt out), `/api/ext/health`. |
| Studio app, Council seat, Registry entry | Same id everywhere. |

Then wire what the app needs from the engine catalogue (§E): SAIG Mail sender, SAIG Pay, SAIG Sign, SAIG Verify, SAIG Voice, SAIG Hello, SAIG Analytics.

**Studio builders get the same birth, ring-fenced.** An app built in SAIG Studio by anyone else gets the same set on SAIG's own infrastructure (SAIG Vault for code, SAIG Data, SAIG Runner, SAIG Cloud), but in **their** space: their own Studio, their own Council and seats, their own registry. Nothing mixes with the estate's or another builder's. They can host on SAIG Cloud or connect their own Vercel.

## E. The engine catalogue: build on our own technology (guide v12)
SAIG runs on its own engines. **Every app, and every app Studio builds, uses these before any third-party service.** If an engine is missing something you need, ask the console on the Council; don't add a vendor. The machine-readable list is `manifest.json → engines`.

| Engine | What it does | Instead of | How to use it |
|---|---|---|---|
| **SAIG AI** | One gateway to many AI models with routing, fallbacks and metering: text, vision, voice, search and images. Tiers: Spark (free models), Summit (best quality), Shield (bank-grade: approved providers only, UK-side pseudonymisation, training opt-out). Includes SAIG Core, the group's own open models on its own servers. | Separate OpenAI / Anthropic / Google accounts and keys in every app | `POST /api/ai?op=complete | stream | transcribe | embed | image` |
| **SAIG Identity** | One account across the estate: sign-in (inline panel), business accounts and teams, consent between apps, and the brain (each person's or business's data) that apps share with permission. | Auth0, Clerk, Firebase Auth | `SAIG sign-in panel (saig-signin.js) · OAuth · /api/saig/identity` |
| **SAIG Data** | Postgres databases and encrypted file storage for every app, on the group's own servers in Germany, one isolated database per app, with nightly encrypted off-site backups. | A separate Supabase or AWS database per app | `SAIG_DATA_URL + REST · storage` |
| **SAIG Schema** | Applies each app's numbered database changes (migrations) safely, in order, once only, and stops at the first error. Records what ran; baseline for databases that already exist. | GitHub Actions deploy pipelines and the Supabase CLI | `POST /api/ext/schema?op=run | status | history | baseline` |
| **SAIG Migrate** | Reads a business's old spreadsheets (Excel, CSV, Google Sheets), works out what every tab is, cleans messy dates, money and phone numbers, flags problems row by row, shows a plan for approval, then imports into the app and reconciles every record. Refuses passwords and card numbers. | Manual imports, consultants and one-off scripts | `POST /api/datamigrate?op=jobs | plan | replan | approve | result` |
| **SAIG Cloud** | The group's own dedicated servers in Europe: databases, AI models, browsers, media and build runners. Every app's scheduled jobs run here on time, and builds run on our own runners at no per-minute cost. | GitHub Actions minutes and schedules, Vercel cron, rented servers | `Scheduled jobs: post a job table on the Council (guide §7c)` |
| **SAIG Council** | How every app in the estate asks, answers and decides, with a full record. The Autopilot answers small questions automatically while an app's chat is closed and queues real work for its next session. | Slack, email threads and manual coordination between teams | `Council API: POST /api/council-api?op=post` |
| **SAIG Memory** | A searchable memory of every app's documents and every Council decision, kept up to date hourly, so any app or agent can answer from what the estate already knows. | Notion, Confluence and internal wikis | `SAIG AI knowledge search` |
| **SAIG Mail** | Business mailboxes, webmail and sending on the group's own mail servers; apps send and read through grants. | Google Workspace / Postmark / SendGrid | `POST https://sws.skylite.group/api/mail?op=send | search` |
| **SAIG Web Services** | Domains, DNS and hosting under one roof. | Registrar dashboards and separate DNS hosts | `SWS API` |
| **SAIG Pay** | Payments, payouts, billing and crypto (USDT/USDC on Solana) for every app. | Stripe dashboards per app | `pay.skylite.group/api/pay` |
| **SAIG Sign** | Legally binding e-signatures with audit trails, executed PDFs and recipient fixes. | DocuSign | `/api/sign` |
| **SAIG Verify** | Identity (KYC) and business (KYB) verification. | Onfido / Persona | `verify.skylite.group` |
| **SAIG Voice** | Speech-to-text, dictation and natural voice. | Separate speech vendors per app | `SAIG AI op=transcribe / voice` |
| **SAIG Meet** | AI-native video meetings on our own media server, with live captions and notes. | Zoom / Teams | `/api/meet` |
| **SAIG Desk** | Remote desktops and real browsers on our servers: supervised form-filling, uploads, UX review. | AnyDesk and browser-automation SaaS | `/api/browser` |
| **SAIG Sentinel** | Monitoring, error tracking, security scanning, pen testing and UX review for every app. | Sentry / Datadog / external pen-testers | `/api/sentinel` |
| **SAIG Hello** | One AI agent for every channel: chat, WhatsApp, Instagram, email and phone, with tickets and leads. | Intercom / Zendesk / Superchat | `hello.skylite.group` |
| **SAIG Tag** | QR and NFC identity linking the physical world to apps. | QR SaaS | `SAIG Tag API` |
| **SAIG Ads** | The ethical, no-tracking ad network. | Google AdSense | `ads.skylite.group` |

## 0b. Ethos and safeguards: every app, from today (v10, founder directive 1 Oct 2026)
SAIG Technologies is an **ethical technology company working towards B Corp standards**. This is not a slogan; it binds every app and every change. The rules are in `manifest.json → ethos.rules`; in short:
- Never sell, share or monetise personal data; no third-party tracking, no ad-tech cookies, no data brokers.
- Prefer SAIG's own infrastructure (mail, domains, DNS, hosting, data, sign, meet, pay, ads, AI on own servers). Use a big-platform service only where reasonably necessary, disclose it, minimise the data it sees, and keep a plan to replace it.
- No Google Ads / AdSense or any surveillance advertising. Advertising only through SAIG Ads.
- Refuse unethical business: no listing or selling of unethical or haram products on commerce apps; no unethical sites built on SAIG Studio; no interest-based (riba) products without Sharia review; no gambling, adult content, weapons, tobacco or alcohol promotion, scams or misinformation.
- Safeguards are built in, not bolted on: every app that lets users publish, list, sell or advertise screens it against these rules (SAIG AI moderation), with a human or SAIG Noor / SKYL review for edge cases.
- Resilience and protection: bot protection, abuse prevention, security by default (SAIG Sentinel), data stays in the regions we promise.
- Give back: a share of profits goes to The Abdul Majid Foundation.
- Advisers: SAIG Noor (ethics and Sharia) and SKYL (law) review questionable cases; when in doubt, leave it out.

**What you must build:** if your app lets anyone publish, list, sell, advertise or build (stores, listings, sites, ads, posts), screen it on the way in with SAIG AI moderation against these rules, block clear breaches, and send edge cases to a person or to **SAIG Noor** (ethics, Sharia) / **SKYL** (law). Record decisions. If your app uses a big-platform service, list it in `SAIG_INTEGRATION.md` under *Third parties* with why it is necessary and what data it sees.

## 0. What SAIG is
SAIG is an ecosystem of specialist AI ventures (Hisaab, SCORRE, PulseAI, Synqro, SKYM,
LifeHQ, NextLayer 3D, Skylite Financial, Skail, **BitPak**, **Worth**, **Tapline**) unified by a
shared identity + intelligence layer.
The **SAIG Console** (`console.skylite.group`) is the platform: it owns identity, consent,
and (soon) AI Employees. **Skye** is the orchestration layer.

Two laws that bind every app:
- **Finance law** — Hisaab COMPUTES, everyone else PRESENTS. Never recompute finances.
- **Content-isolation** — data stays within a brain's scope; personal learning never
  crosses brains; global learning is content-free.

---

## 1. Identity service (the spine)
Base: `https://console.skylite.group/api/saig/identity`
Auth (server-to-server, SERVER-SIDE ONLY): header `x-saig-service-token: <SAIG_SERVICE_TOKEN>`
Ops via `?op=`:

| op | method | who | purpose |
|---|---|---|---|
| `resolve` | GET | service or user | get a `brainId` (+ `kind`) for an auth user or app-local id |
| `link` | POST | service | link your app's local user id to a brain (returns `brainId` + `kind`) |
| `consent_check` | GET | service | is `{fromApp → toApp : scope}` allowed for a brain? |
| `consent_grant` | POST | service or user | grant a scope |
| `consent_revoke` | POST | service or user | revoke a scope |
| `consent_list` | GET | user | the caller's active grants |
| `set_kind` | POST | service or user | set a brain's kind to `human`/`business` |

A user session token (`Authorization: Bearer <central session>`) acts only on the
caller's own brain. Apps generally use **service mode**.

### 1.1 Managed brains (firm-managed, login-less companies)
Some apps create entities for real companies that have **no SAIG login** — e.g. an accountant adds a
client company in Hisaab. The app mints a local `brain_id` and keeps working offline. To keep the spine
authoritative, the app registers that brain so the spine **adopts the local id as canonical** (deduped on
an external key such as a Companies House number). The id never has to change; a login is attached later.

| op | method | who | purpose |
|---|---|---|---|
| `register_brain` | POST | service | record a managed business brain; adopts the sent `brainId` as canonical, dedups on `externalRef`. Idempotent. → `{ brainId, adopt, claimed }` |
| `resolve_ref` | GET | service | canonical brain for an `externalRef`. → `{ brainId, claimed }` or `404` |
| `claim_brain` | POST | service | attach a login (`authUserId`) to a managed brain when the company joins SAIG; brain_id stays stable. → `{ ok, brainId }` or `409` merge required |

`register_brain` body: `{ brainId, kind:"business", externalRef, displayName, sourceApp }`. A response of
**`adopt:true`** means another app registered this company first — persist the returned `brainId` onto your
entity (rare). `claim_brain` body: `{ authUserId, externalRef }` (or `brainId`); it returns **`409` `merge
required`** (with `conflictBrainId`) rather than silently merging a login that already has linked apps.

Managed brains are rows in `saig_identities` with `auth_user_id = null` (plus `external_ref`, `source_app`).
The id Hisaab wrote into its own records stays valid throughout — no rewrite on claim.

---

## 2. Sign in with SAIG (SSO)
All apps authenticate via the SAIG OIDC provider (`custom:saig` in Supabase).
- **Scopes gotcha:** the custom provider's scopes MUST be `openid, email, profile`. The
  dashboard default `openid` alone returns no email and breaks account creation.
- The OIDC `sub` of a user's SAIG identity = the canonical SAIG user id (what the console
  keys brains on).

### 2a. The sign-in is ON the page: no "Continue with SAIG" click, no "Authorize" step (v17, founder rule 8 Oct 2026)
Wherever your app asks someone to sign in, SAIG's own form is already there on your page: **Google, Microsoft, email, password and "Create account"**. Nobody clicks a button to reach it, and nobody approves anything afterwards.
```html
<script src="https://skylite.group/saig-signin.js"></script>
<div data-saig-signin-inline="/api/auth?op=start&next=/where-they-were"></div>  <!-- your existing start URL -->
```
- The value is the URL your app already uses to start SAIG sign-on (it redirects to the authorize URL). Nothing about your OAuth client, PKCE or callback changes. Or call `SAIGSignIn.mount(el, url)`.
- **No consent screen:** SAIG apps are first-party, so the console approves automatically (console `98c21aa`). The screen only says "Signing you in to X as you@… · Not you? Switch account".
- **Switch account works:** it restarts a fresh sign-in instead of reusing the old request (that was the "Not authorised" / "Invalid authorization code" bug).
- **"Use a different account"** link in your app: clear your own session, then send the person to `https://console.skylite.group/oauth/switch?return=<your sign-in page, https>`. That signs them out of SAIG and returns to your page.
- Your callback's error pages must have a `<title>` containing "failed", "error", "expired", "cancelled" or "not authorised", so the embedded form stops instead of retrying.
- **It wears your app's look (v1.2):** the form takes your page's background, text and accent colours and shows **your app's name and logo** at the top ("Sign in to SCORRE · with your SAIG account"). No second header, no scroll box: it grows to fit. Pass `data-app-name`, `data-logo`, `data-accent` on the div (or `{ name, logo, accent }` to `mount`) if you want to set them exactly.
- **Apps that sign in through their own Supabase (`custom:saig` provider)**, like SCORRE: add a path that only starts the SAIG sign-in (e.g. `/auth/saig-start` → `supabase.auth.signInWithOAuth({ provider: 'custom:saig', options: { redirectTo: <an allowed address> } })`), mount the form with that path, and treat any page of yours that loads *inside the frame* as the hand-back (wait for the session, then the host page moves on via `onSignedIn`). Add `'self'` to your CSP `frame-src`. Reference: SCORRE `src/components/SaigSignIn.js` + `src/index.js`.
- **Never** the old pop-up window or modal, and never a "Continue with SAIG" button.
- Reference implementations: scorre.app/login, registry.skylite.group/register and portfolio.saig.page/edit.

---

## 2b. Customer sign-in: email + 6-digit code (founder directive, 30 Sep 2026)
Two doors on every app's sign-in page:
- **The business that runs the app signs in with SAIG** (§2): SKYF's practice, Synqro's shop
  owner, MinaOS's agency. "Continue with SAIG".
- **A customer of that business signs in with NO password and NEVER with SAIG:** email address →
  6-digit code sent by email → session. Label it "Client login" / "Customer login".

The pattern that works on the estate's Supabase plan (SKYF shipped it, `skylitefinancial ab8274a`,
live at `/portal/login`; verified end to end: request → SAIG Mail delivery in ~3 s → verify → session):
1. `POST /api/portal/otp { email }` — only addresses already on a customer record get a code; reply
   is identical either way; rate-limit 5 per 10 min per address and per IP.
2. Server: `supabase.auth.admin.generateLink({ type: "magiclink", email })` → `properties.email_otp`
   (6 digits). Project auth config (management API): `mailer_otp_length: 6`,
   `external_email_enabled: true`. **Send the code yourself through SAIG Mail** (`op=send`,
   mailbox = your app's business mailbox, no signature) — the project mailer is capped at 2/hour
   and its template can't be changed on the free plan.
3. Browser: `supabase.auth.verifyOtp({ email, token, type: "email" })` → session; then your
   normal customer mapping by email.
Never expose SAIG SSO to a customer, and never issue a code to an email that is not already a
customer of that business.

---

## 3. Brain link on login (EVERY app does this)
On the SERVER, after SSO, reconcile the user to their canonical `brain_id`:
1. Derive the SAIG subject from the verified session (the OIDC `sub` of the user's
   `custom:saig` identity — in Supabase, the `id` of that identity). Never trust a
   client-supplied value.
2. Call once (idempotent):
   ```
   POST {identity}?op=link
   headers: x-saig-service-token: <SAIG_SERVICE_TOKEN>
   body: { "authUserId": "<SAIG sub>", "app": "<yourApp>", "localUserId": "<your local user id>" }
   -> { "ok": true, "brainId": "...", "kind": "human" | "business" }
   ```
3. Store the returned `brainId` (and `kind`) locally; use `brainId` as your cross-app key.
   Keep your own `user_id` for local access control only.

`brain_id` = whose reality the data belongs to. `user_id` = access control only.

---

## 4. Account type (kind)
SAIG accounts choose **Individual** or **Business** at signup (only on the SAIG signup
page — do NOT add your own selector). The console mints the matching brain kind:
`individual → human`, `business → business`.

`resolve`/`link` return `kind`. Store it and branch:
- `business` → company/org context, business tier, company-level data.
- `human` → individual.
Absent (older accounts) → treat as `human`. Change later via `set_kind`.
Full reference: https://skylite.group/ecosystem/SAIG_ACCOUNT_TYPES.md

---

## 5. Consent (before any cross-app data)
A brain authorises `{fromApp → toApp : scope}`. Nothing crosses without a grant.
- **Granting (user connects app B in app A):** app A calls (service mode)
  `POST {identity}?op=consent_grant { brainId, fromApp:"A", toApp:"B", scope:"..." }`.
- **Serving (app B before returning data):** app B calls
  `GET {identity}?op=consent_check&brainId=..&fromApp=A&toApp=B&scope=..` → `{ allowed }`.
  If `false` → 403.

Scopes (owned/documented by each app): `finance:read`, `finance:submit`, `accounts:read` (Hisaab),
`qa:score`, `qa:read` (SCORRE), `commerce:read/write` (Synqro), `kyc:verify/read` (KYC),
`knowledge:read`. See the spec for the full taxonomy.
- `accounts:read` (Hisaab) — read a brain's classified trial balance / statutory-accounts data via `/api/ext/trial-balance` (consumed by Skylite Financial); fails closed (403) without the grant.

---

## 6. /api/ext — cross-app data surfaces

**Required (v3): SAIG Health.** Every app exposes `GET /api/ext/health` in two modes,
both gated by `x-saig-service-token`:

- **Shallow (no params)** — liveness, unchanged: `{ ok, app: "<slug>", guideVersion, ts }`.
- **Deep (`?deep=1`)** — the full health battery returning the standard **Snapshot**
  (specVersion 1): env presence booleans (names only, never values), identity-spine
  checks, an honest self-test (uniform check shape `{id,label,status:green|amber|red|skipped,detail?,remedy?}`
  — a check that cannot run reports `skipped` with a remedy, never a false green),
  dependency checks (key-present vs actually-working), and recent errors (≤300 chars,
  no secrets). Full contract, self-test steps, diagnostic-report format and the Console
  Health Hub behaviour: https://skylite.group/ecosystem/SAIG_SYSTEM_HEALTH_SPEC.md

The Console Health Hub (console.skylite.group/diagnostics) fans out to every app's
health URL (now listed per venture in the manifest), stores org-wide snapshots, and
shows liveness-only apps as amber until they implement `?deep=1`. Local admin health
pages (Pulse `/app/debug` is the reference) remain the app-level view.
If your app **exposes** data to the ecosystem, expose it under `/api/ext/*` per the
contract: service-token verifier, `X-SAIG-App` (caller) + `X-SAIG-Brain` headers,
`consent_check` before serving, idempotency on `X-SAIG-Event-Id` for writes, and a signed
completion event on writes. If your app **consumes** another's data, call its `/api/ext/*`
server-side with your service token + `X-SAIG-App` + the canonical `brainId`.
Full contract (envelopes, error codes, per-venture surfaces):
https://skylite.group/ecosystem/SAIG_EXT_API_CONSENT_AND_TOOL_REGISTRY_SPEC.md

---

## 6b. New engines and apps (August 2026)

Three ventures joined in the week of 16–18 August. **Two of them are engines you can consume.**

| Venture | What it is | Consume it via |
|---|---|---|
| **Tapline** `tapline.skylite.group` | **An engine.** A printed QR/NFC code becomes a direct line to a human — two-way video in the browser, no hardware, no phone numbers, no app for whoever scans. | `POST /api/tapline?op=issue { ownerBrain, kind, label, behaviour, appKey, count }` → codes, URLs, QR images. Behaviours: `call` · `lost_found` · `message` · `info` · `checkin` (deep-links back into YOUR flow). Health: `/api/ext/health`. |
| **BitPak** `bitpak.skylite.group` | Client crypto portfolios and a PKR-first exchange for Pakistan. Entity **Skylite Consulting (Pvt) Ltd**, NOT Skylite Group Ltd. | Not yet a general engine. **⚠️ No PVARA licence or NOC is held — no surface may render BitPak as "licensed".** SAIG Pay's Crypto tab must stay invisible to anyone who is not already a BitPak client. |
| **Worth** `worth.skylite.group` | Consent-first personal data: see what companies hold on you, decide, and get paid if you choose to sell. | Engine (**SAIG Consent**) is spec, not built. Waitlist API live. |

**Tapline is the one to look at.** If your app has a physical touchpoint — a door, a room, a
vehicle, an asset, a keyring — you can issue tags against your own `appKey` and store no tag state
yourself. @samaconcierge: a short-stay guest arriving at a property with no doorbell scans and
video-calls the host, with no numbers exchanged.

---

## 7. Environment variables
Every app:
```
SAIG_IDENTITY_URL  = https://console.skylite.group
SAIG_SERVICE_TOKEN = <shared value, identical across the console + all apps>   # server-side only
```
Finance consumers (e.g. LifeHQ) additionally:
```
HISAAB_API_URL       = <Hisaab base>
HISAAB_SERVICE_TOKEN = <Hisaab's inbound /api/ext token>
# ⚠️ NEVER code a fallback of HISAAB_SERVICE_TOKEN || SAIG_SERVICE_TOKEN — Hisaab REJECTS the
# shared estate token (401). Silent fallback + best-effort emit = invisible data loss (#156).
```
`SAIG_SERVICE_TOKEN` must be byte-identical everywhere or calls 401.

---

## 7b. Database: every new table in `public` needs explicit GRANTs (from 30 Oct 2026)

Supabase stops auto-granting Data API access to **new** tables in the `public` schema on
**30 October 2026**, on every project (existing projects included). Existing tables keep their grants;
nothing breaks today. From that date a table created without grants is unreachable through
supabase-js / PostgREST — including through the **service role** — and the call fails with
`permission denied`.

**Rule (effective now, so migrations written today still work after 30 Oct):** the migration that
creates a table in `public` also grants on it, only to the roles that need it. Keep RLS on.

```sql
create table public.example (...);
alter table public.example enable row level security;
-- server-only table (the usual case in this estate):
grant select, insert, update, delete on public.example to service_role;
-- add ONLY if the browser reads it under RLS:
-- grant select, insert, update, delete on public.example to authenticated;
-- grant select on public.example to anon;
```

This also applies to preview branches and a local `supabase db reset`. Do **not** restore the old
behaviour with `alter default privileges … to anon, authenticated` — least privilege is the point.

## 7c. Scheduled jobs run on saig-runner, never on GitHub (founder directive 1 Oct 2026)
GitHub Actions minutes are off for the whole estate, and GitHub's `on: schedule` is unreliable (MinaOS saw */15 jobs never fire). **Never use `on: schedule`, pg_cron inside your app, or Vercel cron for estate jobs.** Every recurring job runs on **saig-runner's own scheduler** (systemd timers):
- **HTTP jobs:** an idempotent authenticated `POST` to your app (your `SAIG_APP_TOKEN` in `x-saig-service-token`). Ask the console on the Council with a table: job name, schedule (UTC or Europe/London), URL. Live within a day, read back on the thread.
- **Code jobs** (scripts that need a checkout): the seat-job runner runs `<command>` in a fresh checkout of your repo with your seat env. Same ask, plus the env names you need.
- Jobs are `Persistent` (a missed run catches up after a reboot) and their results are in the runner journal; ask for a read-back any time.

## 8b. Email: everything goes through SAIG Mail (founder directive, 24 Sep 2026)

SAIG Mail (`saigmail`) is the **only** email system in the estate: sending (transactional and
person-to-person), receiving, mailboxes, signatures, drafts, search, inbound webhooks and the
**authentication emails** (sign-in links, password resets, invites). This covers every existing app,
every new app, and everything SAIG Studio generates.

- **Do not** call Resend, SendGrid, Postmark, Mailgun, Amazon SES or any SMTP server from an app. Do
  not add new keys for them. Existing ones are removed per app once its SAIG Mail path is proven.
- **Send / read / sign** through SAIG Mail's API (`https://saigmail.skylite.group/api/…`, contract on
  Council #998): estate service token + `X-SAIG-App: <your id>`.
- **Auth emails:** every Supabase project's SMTP points at SAIG Mail's SMTP relay. No Supabase
  built-in mailer, no project-level SES.
- **Inbound:** replies and inbound mail reach apps by SAIG Mail's signed webhook
  (`x-saig-timestamp`, `x-saig-signature: sha256=HMAC(token, ts + "." + rawBody)`), never by an app
  logging into a mailbox itself.
- Console's `/api/mail/send` and `/api/postmaster` are **being retired**: during the move they forward
  to SAIG Mail, then return `410 moved_to_saigmail`.

## 8. Shared ecosystem footer
Mount the shared band (auto-updates across the ecosystem):
```html
<div class="saig-ecosystem-footer" data-venture="YourApp" data-accent="#yourhex"></div>
<script src="https://skylite.group/ecosystem-footer.js" defer></script>
```
`data-venture` marks your node "you are here"; `data-accent` blends the orb/hovers to your
brand. Omit `data-venture` on the hub.

---


**The uniform SAIG header and apps grid (v7, 25 Sep 2026).** The footer script now also renders a thin
**SAIG header** at the very top of every app: the SAIG mark and your app's name on the left, the Google-style
**SAIG apps** grid on the right (every app, grouped: SAIG tools · Finance & business · Travel · Property ·
Life & communication · Group & governance). It is **automatic** for any app that loads the footer script.
Choose where it sits, or integrate the grid into your own header instead:

```html
<div class="saig-ecosystem-header" data-venture="YourApp"></div>   <!-- the bar renders here -->
<span data-saig-launcher></span>   <!-- OR: just the grid button inside your own header (no bar) -->
<meta name="saig-header" content="off">   <!-- opt out only with the founder's agreement -->
```

If your header is `position: fixed` at the top, use one of the two mounts above so the bar is not hidden under it.

**Account avatar in the header (v17, founder rule 8 Oct 2026).** Top right, beside the apps grid, the header shows who is signed in to *your* app, with **Switch account** and **Sign out**. The header is shared but the account is yours, so tell it: add `<meta name="saig-account" content="/api/me">` (an endpoint returning `{ signedIn, email, name, photo }`), or call `SaigEcosystemFooter.setUser({ email, name, photo, signOut, switchAccount })` after sign-in (`setUser(null)` after sign-out). Defaults: sign out `/api/auth?op=logout&next=/`, switch `/api/auth?op=logout&switch=1&next=<page>`. Apps that already draw their own avatar in their own header (using `data-saig-launcher`) keep theirs.

Want the old icon band instead of the slim footer? `data-style="full"` on the footer mount.
## 9. Full specifications
- Constitution: https://skylite.group/ecosystem/SAIG_INTEGRATION_BLUEPRINT.md
- Cross-app contract: https://skylite.group/ecosystem/SAIG_EXT_API_CONSENT_AND_TOOL_REGISTRY_SPEC.md
- Account types: https://skylite.group/ecosystem/SAIG_ACCOUNT_TYPES.md
- Backlog & roadmap: https://skylite.group/ecosystem/SAIG_PLATFORM_BACKLOG_AND_ROADMAP.md

---

## 10. Staying in sync (set up once, then it's automatic)
This guide is **versioned** — the current version is in the manifest at
`integration_guide.version`. When it changes, re-read, apply the deltas, then bump the
version your app records as implemented.

**Changelog:** v15 (2026-10-03) — §0c.7 GitHub (User account) + Vercel (team id, commit author) facts; §0c compact, workboard-first council (op=board), efficiency rules. v14 (2026-10-03) — §0c code words build, governance, sentinel; SAIG Blueprint (.saig/BLUEPRINT.md until the API). v13 (2026-10-03) — §0c founder code word "council": full Council sweep from one inbox call. v12 (2026-10-02) — §E engine catalogue (manifest `engines`). v11 (2026-10-01) — §7c: scheduled jobs run on saig-runner, never GitHub `schedule:`. v10 (2026-10-01) — §0b: ethos and safeguards (manifest `ethos`); §0a: also read your Sentinel inbox. v9 (2026-10-01) — §0a: read the announcements (manifest `announcements` + `renames`) at the start of every session and acknowledge each; §10b check warns when any are waiting. v8 (2026-09-30) — §2b: customer sign-in by email + 6-digit code (businesses use SAIG; customers never do). v7 (2026-09-25) — §8: uniform SAIG header (automatic) with the apps grid. v6 (2026-09-25) — §8: slim footer + SAIG apps grid; header slot `data-saig-launcher` required. v5 (2026-09-24) — §8b: all email (send, receive, mailboxes, auth emails) through SAIG Mail only. v4 (2026-09-23) — §7b: explicit GRANTs in the same migration for every new `public` table (Supabase change effective 30 Oct 2026). v3 (2026-07-10) — SAIG Health deep battery required (§6, spec link above). v2 — identity/consent/ext conventions. (22 Sep 2026: §10 snippets repaired — they had been published with a non-JavaScript placeholder in place of `IMPLEMENTED`; no version change.)

**(a) Record the version you implement.** Keep a constant + repo note, e.g.
`SAIG_GUIDE_VERSION = 2`. Bump only when you've re-synced to a newer guide.

**(b) Deploy-time check (recommended).** Warn when the central guide is ahead of what
you've implemented. Add `scripts/saig-guide-check.mjs`:
```js
const IMPLEMENTED = 3; // the guide version THIS app implements — keep equal to SAIG_GUIDE_VERSION
const r = await fetch("https://skylite.group/ecosystem/manifest.json").catch(() => null);
const j = r && r.ok ? await r.json() : null;
const latest = j?.integration_guide?.version;
if (latest && latest > IMPLEMENTED) {
  console.warn(`\n⚠  SAIG integration guide is v${latest}; this app implements v${IMPLEMENTED}.`);
  console.warn(`   Re-read ${j.integration_guide.url} and bump IMPLEMENTED.\n`);
  // To hard-block deploys until re-synced: process.exit(1);
}
// v9: announcements waiting? (see §0a)
import fs from "node:fs";
let seen = 0; try { seen = JSON.parse(fs.readFileSync(".saig/announcements.json", "utf8")).last_seq || 0; } catch {}
const waiting = (j?.announcements?.items || []).filter((a) => a.seq > seen);
if (waiting.length) {
  console.warn(`\n📣 ${waiting.length} SAIG announcement(s) not yet acted on:`);
  for (const a of waiting) console.warn(`   #${a.issue} ${a.title} → ${a.action}${a.deadline ? " (by " + a.deadline + ")" : ""}`);
  console.warn(`   Act, ack on each thread, then set last_seq in .saig/announcements.json.\n`);
}
```
Wire it into `prebuild`/`prepush`, e.g. `"prebuild": "node scripts/saig-guide-check.mjs"`.

**(c) Runtime freshness banner (optional, drop-in).** The shared footer publishes the
current guide version on `window.__saigEcoFooter.ecosystem`. Paste once:
```html
<script>
(function () {
  var IMPLEMENTED = 3; // the guide version THIS app conforms to — keep equal to SAIG_GUIDE_VERSION
  function show(eco) {
    if (!eco || !eco.guideVersion || eco.guideVersion <= IMPLEMENTED) return;
    console.warn("SAIG integration guide v" + eco.guideVersion + " > implemented v" + IMPLEMENTED + " — " + eco.guide);
    var devOnly = /^(localhost|127\.0\.0\.1)$/.test(location.hostname) || localStorage.getItem("saig_show_guide_banner") === "1";
    if (!devOnly) return; // never shown to normal end-users
    var b = document.createElement("div");
    b.textContent = "SAIG integration guide updated to v" + eco.guideVersion + " — review " + eco.guide;
    b.style.cssText = "position:fixed;bottom:0;left:0;right:0;z-index:99999;background:#7c5cff;color:#fff;font:600 12px/1.4 system-ui;padding:8px 14px;text-align:center";
    document.body.appendChild(b);
  }
  var n = 0, t = setInterval(function () {
    var eco = (window.__saigEcoFooter || {}).ecosystem;
    if (eco) { show(eco); clearInterval(t); } else if (++n > 20) clearInterval(t);
  }, 500);
})();
</script>
```

*This guide supersedes older pasted notes. When it changes, you'll simply be asked to
re-read it.*
