> ## Documentation Index
> Fetch the complete documentation index at: https://docs.switchbord.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Architettura di Vault

> Un riferimento tecnico approfondito su come Supabase Vault è integrato in Switchbord — che copre il modello di memorizzazione, i percorsi di lettura e scrittura, la crittografia, il controllo degli accessi e il livello RPC SECURITY DEFINER.

# Architettura di Vault

Questa pagina è un riferimento tecnico per operatori e contributori attenti alla sicurezza che desiderano comprendere in modo approfondito come funziona l'integrazione del vault di Switchbord. Per le istruzioni di configurazione, vedi [Configurazione di Vault](/it/operations/vault-configuration).

***

## Modello di Memorizzazione

Switchbord utilizza due livelli di oggetti del database per memorizzare i segreti del workspace:

**`vault.secrets` (integrato in Supabase)**
I valori dei segreti effettivamente crittografati risiedono qui. Ogni riga contiene una colonna `secret` crittografata con pgsodium (AES-256-GCM), una colonna `name` per l'identificazione leggibile e un `key_id` che fa riferimento alla chiave pgsodium utilizzata per la crittografia. Le righe sono identificate da un UUID.

**`app_private.workspace_secret_bindings` (personalizzata di Switchbord)**
Questa tabella mappa un workspace ai suoi segreti:

```
workspace_secret_bindings
  id              uuid PRIMARY KEY
  workspace_id    uuid NOT NULL  -- references workspaces(id)
  secret_kind     text NOT NULL  -- 'meta-access-token', 'openai-api-key', etc.
  vault_secret_id uuid NOT NULL  -- references vault.secrets(id)
  created_at      timestamptz
  updated_at      timestamptz
  UNIQUE (workspace_id, secret_kind)
```

La tabella dei binding non contiene di per sé dati sensibili. È puramente un puntatore: per il workspace X e il tipo di segreto Y, individua il segreto vault Z. La decrittografia effettiva avviene in `vault.secrets`.

***

## Perché lo Schema `app_private`

Lo schema `app_private` è utilizzato per tabelle e funzioni che non devono essere esposte tramite PostgREST (l'API REST auto-generata di Supabase). Per impostazione predefinita, PostgREST espone solo gli oggetti nello schema `public` (e gli schemi esplicitamente elencati in `db_schema`). Il posizionamento di `workspace_secret_bindings` in `app_private` garantisce che:

* Nessun client anonimo o autenticato possa eseguire direttamente una `SELECT` sulla tabella dei binding tramite la libreria client di Supabase
* Non venga generato alcun endpoint REST per `/app_private/workspace_secret_bindings`
* L'accesso sia possibile solo tramite funzioni esplicitamente definite eseguite come `SECURITY DEFINER`

La row-level security è anch'essa attivata su `workspace_secret_bindings` come misura di defense-in-depth, ma il posizionamento nello schema `app_private` è il livello di isolamento primario.

<Info>
  Il pattern dello schema `app_private` è una convenzione standard di Supabase per gli oggetti di schema sensibili. È documentato nelle guide di Supabase su "Row Level Security" e "Functions" come il modo consigliato per proteggere dati che non dovrebbero mai essere accessibili direttamente.
</Info>

***

## Architettura del Percorso di Lettura

Il percorso di lettura è progettato per evitare di richiedere una connessione diretta al database dal servizio worker. Tutte le letture sensibili passano attraverso funzioni RPC di Supabase definite con `SECURITY DEFINER`.

```
Worker service
    |
    | supabase.rpc('sb_read_workspace_secret', { p_workspace_id, p_kind })
    |
Supabase PostgREST (REST API layer)
    |
    | calls public wrapper function
    |
public.sb_read_workspace_secret(p_workspace_id uuid, p_kind text)
    |   [SECURITY INVOKER, checks caller is service_role]
    |
    | calls internal function
    |
app_private.read_workspace_secret(p_workspace_id uuid, p_kind text)
    |   [SECURITY DEFINER, runs as owning role]
    |
    | SELECT decrypted_secret FROM vault.decrypted_secrets
    | JOIN app_private.workspace_secret_bindings ...
```

### Funzioni `SECURITY DEFINER`

Per garantire che gli elementi del vault non vengano mai esposti tramite lo schema `public` standard o gli endpoint REST auto-generati, Switchbord utilizza il pattern dello schema `app_private`.

**`app_private.read_workspace_secret`**
Unisce `app_private.workspace_secret_bindings` con `vault.decrypted_secrets` per restituire il valore in chiaro. Viene eseguita come `SECURITY DEFINER`, consentendole di accedere allo schema `vault`, normalmente limitato.

**`app_private.resolve_workspace_by_verify_token`**
Utilizzata durante la verifica del webhook Meta per instradare le richieste al workspace corretto senza esporre i token al client.

### Wrapper RPC Pubblici

Poiché PostgREST espone solo lo schema `public`, forniamo wrapper leggeri che applicano l'autorizzazione:

* **`public.sb_read_workspace_secret`**: valida `auth.role() == 'service_role'` prima di delegare alla funzione privata.
* **`public.sb_resolve_workspace_by_verify_token`**: segue lo stesso pattern di sicurezza per la risoluzione del token.

<Warning>
  Questi wrapper rifiutano esplicitamente le chiamate effettuate con la chiave anon o un JWT con ambito utente. Solo la chiave del ruolo di servizio (`SUPABASE_SECRET_KEY`) può invocarli. Ciò è applicato a livello SQL tramite controlli `auth.role()` all'interno delle funzioni wrapper.
</Warning>

***

## Percorso di Scrittura

Il percorso di scrittura passa attraverso l'applicazione web di Switchbord e non coinvolge il worker:

```
Settings UI (browser)
    |
    | POST /api/settings/secrets  { kind, value }
    |   with user session JWT
    v
Next.js API route
    |
    | validates session, resolves workspace_id
    | calls storeWorkspaceSecret(workspaceId, kind, value)
    v
storeWorkspaceSecret()  (server-side utility)
    |
    | Step 1: supabase.rpc('vault_create_secret', { value, name })
    |   returns vault_secret_id
    |
    | Step 2: INSERT INTO app_private.workspace_secret_bindings
    |   (workspace_id, secret_kind, vault_secret_id)
    |   ON CONFLICT DO UPDATE  (upsert — replaces existing binding)
    v
Secret stored and bound to workspace
```

Il percorso di scrittura utilizza una connessione Postgres diretta (`SUPABASE_DB_URL`) quando invoca `vault.create_secret()` dall'esterno del server applicativo, ma la route API Next.js gestisce questo utilizzando il client con ruolo di servizio di Supabase. Chi effettua il self-hosting non necessita di `SUPABASE_DB_URL` nel worker — solo nell'applicazione web se sono necessarie scritture al vault dal codice lato server.

***

## Crittografia a Riposo

Supabase Vault utilizza [pgsodium](https://github.com/michelp/pgsodium), un'estensione PostgreSQL che avvolge libsodium.

* **Algoritmo:** XChaCha20-Poly1305 (un cifrario a flusso con autenticazione)
* **Gestione delle chiavi:** pgsodium utilizza una chiave radice derivata da un segreto memorizzato fuori dal database (nell'infrastruttura di gestione delle chiavi di Supabase per Cloud, o configurata separatamente per il self-hosting)
* **Ambito della crittografia:** ogni segreto in `vault.secrets` viene crittografato con una chiave derivata dalla chiave radice. Il testo cifrato e l'ID della chiave vengono memorizzati insieme; il testo in chiaro non viene mai persistito.
* **Decrittografia:** la vista `vault.decrypted_secrets` esegue la decrittografia in modo trasparente utilizzando `pgsodium.crypto_aead_det_decrypt()`. Solo i ruoli del database con permesso SELECT su questa vista possono leggere i valori in chiaro.

<Info>
  Per i deployment su Supabase Cloud, la chiave radice pgsodium è gestita da Supabase e non viene mai esposta ai proprietari del progetto. Per Supabase self-hosted, l'utente è responsabile della protezione della chiave radice — vedi la [guida al self-hosting di Supabase](https://supabase.com/docs/guides/self-hosting) per la configurazione delle chiavi.
</Info>

***

## Riepilogo del Controllo degli Accessi

| Livello                            | Meccanismo                               | Effetto                                                                              |
| ---------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------ |
| Schema `app_private`               | Non esposto da PostgREST                 | Le tabelle e le funzioni interne non sono accessibili tramite REST                   |
| RLS su `workspace_secret_bindings` | Row-level security attivata              | Defense-in-depth: i binding hanno ambito limitato al workspace                       |
| Wrapper `public.sb_*`              | Controllo `auth.role() = 'service_role'` | Solo il JWT del ruolo di servizio può invocare le letture del vault                  |
| `vault.decrypted_secrets`          | Ruolo privilegiato richiesto             | Accesso diretto bloccato per i ruoli anon e authenticated                            |
| Funzioni `app_private.*`           | `SECURITY DEFINER`                       | Le funzioni vengono eseguite come owner, non come chiamante — elevazione controllata |

Il worker autentica tutte le chiamate RPC utilizzando il JWT del ruolo di servizio (`SUPABASE_SECRET_KEY`). Questa chiave deve essere mantenuta segreta e non deve mai essere esposta ai client browser o registrata nei log.

***

## Riferimento per la Migrazione

L'integrazione del vault è composta da due livelli:

**Integrato in Supabase (nessuna azione richiesta)**

* Schema `vault` e tabella `vault.secrets`
* Vista `vault.decrypted_secrets`
* Funzione `vault.create_secret()`
* Estensione pgsodium

Questi sono presenti su tutti i progetti Supabase Cloud e possono essere attivati su Supabase self-hosted tramite `CREATE EXTENSION supabase_vault`.

**Personalizzato di Switchbord (applicato tramite migrazioni)**

* Creazione dello schema `app_private`
* Tabella `app_private.workspace_secret_bindings`
* Funzione `app_private.read_workspace_secret()`
* Funzione `app_private.resolve_workspace_by_verify_token()`
* Wrapper `public.sb_read_workspace_secret()`
* Wrapper `public.sb_resolve_workspace_by_verify_token()`

Questi vengono creati dalle migrazioni del database di Switchbord in `supabase/migrations/`. L'esecuzione di `supabase db push` applica tutte le migrazioni in sospeso e garantisce l'esistenza di questi oggetti.

Se è necessario ispezionare direttamente le definizioni delle funzioni:

```sql theme={null}
-- Check the public wrapper exists
\df sb_read_workspace_secret

-- View the function source
SELECT prosrc
FROM pg_proc
WHERE proname = 'sb_read_workspace_secret';
```

***

## Considerazioni sulla Sicurezza per chi Effettua il Self-Hosting

**Esposizione della chiave del ruolo di servizio**
La `SUPABASE_SECRET_KEY` può invocare qualsiasi funzione RPC e ignora la RLS. Trattarla con la stessa cura di una password root del database. Ruotarla immediatamente in caso di sospetta esposizione.

**Chiave pgsodium di Supabase self-hosted**
Se si esegue Supabase localmente o sulla propria infrastruttura, la chiave radice pgsodium deve essere impostata prima che venga memorizzato qualsiasi segreto. Se la chiave cambia o viene persa, tutti i segreti vault memorizzati diventano irrecuperabili — non esiste alcun key escrow.

**Integrità delle migrazioni**
Le funzioni `SECURITY DEFINER` in `app_private` sono sicure solo quanto lo è il ruolo proprietario del database. Assicurarsi che solo le parti fidate abbiano la possibilità di ridefinire o sostituire queste funzioni tramite migrazioni.

***

## Guide Correlate

* [Configurazione di Vault](/it/operations/vault-configuration) — configurazione per il self-hosting, variabili d'ambiente e risoluzione dei problemi
* [Segreti e Crittografia](/it/security/secrets-and-encryption) — politica di sicurezza, hashing delle API key e header di sicurezza HTTP
* [Multi-Tenancy](/it/security/multi-tenancy) — modello di isolamento dei workspace
* [Autenticazione](/it/security/authentication) — gestione del JWT e modello di sessione
