Content Studio — infra da provisionare (founder), passo-passo

Il codice del routing sovrano è in PR #145 (repo Multiverbe) ed è fail-closed: finché queste env non ci sono, la generazione risponde 503 sovereign_routing_required (mai fallback a un endpoint consumer). Appena le imposti, si attiva da sola. I nomi delle env qui sotto sono esattamente quelli letti dal codice.

🔵 Stato 2026-06-04 (sessione "sblocco infra" · verifica REALE pre-azione)

Probe GET /api/content/sovereign-status (live) + sovereignty_register_v verificati ORA, non dalle note. Stato vero:

  • Testo: vertex_gemini=true · vertex_claude=true ✅ — mistralVERIFICATO LIVE 2026-06-04 (key multiverbe-prod creata + env MISTRAL_API_KEY Production+Preview + redeploy prod → probe mistral:true). Outreach sbloccato. Resta compliance: (a) toggle training Mistral era ON → mettere OFF (/plateforme/privacy); (b) ZDR + DPA = richiesta a Mistral (contract-level).
  • Immagini: imagen_fast=true · imagen_ultra=true ✅ — flux=false (opzionale, P5).
  • Provenance: c2pa_signing=falseleggi la nota P2 qui sotto: NON provisionare il cert adesso.
  • Register sovereignty_register_v = 0 righe. I provider sono configurati (probe verde) ma nessuna generazione reale è ancora passata dalla UI: lo farà la sessione Content. NON è un blocco infra.

⚠️ P2 — C2PA cert: NON ancora provisionabile (finding di questa sessione). Nel codice non esiste un firmatario C2PA reale: nessuna dipendenza c2pa-node/c2patool in package.json, e isC2paSigningConfigured() si limita a flippare signed:true nel manifest (content-provenance.ts). Impostare C2PA_SIGNING_CERT/_KEY adesso marcherebbe i manifest come signed:true senza firmarli davvero = dichiarazione di provenienza falsa (peggio dell'onesto signed:false, e contro la stessa logica anti-claim di SOV7). Sequenza corretta: prima si implementa il firmatario reale (c2pa-node + cert X.509 + embedding), poi si provisiona il cert. Fino ad allora il cert resta OFF (manifest signed:false, già il comportamento corretto). → cert path documentato sotto, ma gate = signer implementato.

✅ CHIUSURA sessione (pomeriggio 2026-06-04) — Chrome connesso, eseguito dal vivo:

  • P1 Mistral COMPLETO: key live + training toggle OFF (era ON) + DPA in vigore + richiesta ZDR pronta (mistral-zdr-dpa-request-2026-06-04.md).
  • ⛔ P4 video / P5 Azure FLUX / P6 voce = PREMATURI (stessa classe di C2PA): adapter inesistenti nel codice (dispatchAudio/Video assenti, azure-flux lancia image_adapter_not_implemented). NON creare account/pagare: il blocco è codice (adapter), consegnato a Content/Video. Mettere AZURE_FOUNDRY_* ora introdurrebbe un bug latente nel fallback immagini.
  • P3 DPA COMPLETO: Mistral + Google CDPA in vigore by-reference (progetto multiverbe-2606030125 confermato); sub-processor list arricchita con le DPA URL verificate → PR #172.
  • P7 SOV7 GREENLIT (founder, incl. outreach): clausola L8 ToS redatta in sov7-claim-l8-tos-2026-06-04.md; gate pre-pubblico = legale CH + L8 + conferma ZDR.
  • P8 eseguito: PR #170 (commitlint prefix:"chore" + deps-dev enum) + 6 PR superseded chiuse. Verde sui check reali, pronta da mergiare. Post-merge: @dependabot recreate su #162/#165/#166/#167.

✅ Stato 2026-06-03 (sera) — Vertex EU VERIFICATO LIVE (text + image)

#155/#159 mergiati → Content sovrano in prod. Probe GET /api/content/sovereign-status = vertex_gemini/claude/imagen true · ready.text/image true. GCP Vertex confermato attivo (il punto A sotto è CHIUSO). Restano per la sessione "sblocco infra" (content-handoff-prompts-2026-06-03.md · prompt B): B1 MISTRAL_API_KEY (probe mistral:false → outreach 503) · C2PA signing cert (C2PA_SIGNING_CERT/_KEY) · provider video (DEC#2 Synthesia/Runway/Firefly) · DPA (Google/Mistral/Azure/fal) · SOV7. Migrazione content_brand_signals applicata (oltre a a→e).

⏳ Stato 2026-06-03 (mattina) — handoff verifica (storico, ora superato)

Il founder ha già provisionato la parte GCP, da verificare l'accensione:

  • Progetto GCP creato: multiverbe-2606030125 · region europe-west4 · billing attivo · service account vertex-runtime@multiverbe-2606030125.iam.gserviceaccount.com + ruolo roles/aiplatform.user.
  • Le 4 env (GCP_VERTEX_PROJECT/LOCATION/SA_CLIENT_EMAIL/SA_PRIVATE_KEY) inserite in Vercel (multiverbe-app, prj_yWNM0v8ejuLHtLCLXZvSKvHnA4k9). Il primo tentativo era con la chiave incompleta; reinserita.
  • DA VERIFICARE: (1) Redeploy eseguito dopo il salvataggio delle env; (2) una generazione reale dal Pianeta Content (multiverbe.app). Finora content_generations = 0 righe, log runtime vuoti → mai testato.
  • Come confermare dal MCP (in 10s, senza login utente): SELECT created_at, ai_model, provider, provider_region, sovereign FROM public.sovereignty_register_v ORDER BY created_at DESC LIMIT 5; → deve comparire provider = vertex-gemini-eu, sovereign = true. In più i log runtime Vercel (team team_temx7lcMkSnWbL1DrH4KQqlc, project sopra).
  • Se la generazione dà errore 502 invece del post: quasi sempre è la GCP_SA_PRIVATE_KEY incompleta (deve includere righe BEGIN+corpo+END) o il Redeploy non fatto. Se dà il banner 503 sovereign_routing_required: env non attive su Production.
  • Migrazioni a→e: già applicate in prod (questa sessione, via MCP). Advisor sicurezza pulito.

A. Vertex AI EU — testo + immagini (Imagen) · sblocca SOV1 + il motore immagini

Modo veloce (Cloud Shell, dal browser, ~5 min, niente da installare)

Apri shell.cloud.google.com (sei già loggato col tuo account Google), poi incolla questo blocco (cambia PROJECT_ID se vuoi un nome diverso o usa un progetto esistente):

export PROJECT_ID="multiverbe-prod"        # nome progetto (o un id esistente)
export REGION="europe-west4"               # EU Olanda · oppure europe-west1 (Belgio)

# crea il progetto (salta se ne usi uno esistente) + impostalo
gcloud projects create "$PROJECT_ID" 2>/dev/null; gcloud config set project "$PROJECT_ID"
# IMPORTANTE: il progetto deve avere il billing attivo (Console > Billing) o l'API non parte

# abilita Vertex AI
gcloud services enable aiplatform.googleapis.com

# service account + ruolo Vertex
gcloud iam service-accounts create vertex-runtime --display-name="Multiverbe Vertex runtime"
export SA_EMAIL="vertex-runtime@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
  --member="serviceAccount:${SA_EMAIL}" --role="roles/aiplatform.user"

# chiave JSON (resta privata nella tua Cloud Shell)
gcloud iam service-accounts keys create vertex-key.json --iam-account="$SA_EMAIL"

# stampa i 3 valori non-segreti da mettere in Vercel
echo "GCP_VERTEX_PROJECT=$PROJECT_ID"
echo "GCP_VERTEX_LOCATION=$REGION"
echo "GCP_SA_CLIENT_EMAIL=$SA_EMAIL"
echo "--- per GCP_SA_PRIVATE_KEY copia il campo private_key qui sotto (è SEGRETO, va solo in Vercel) ---"
cat vertex-key.json

Poi in Vercel → progetto app → Settings → Environment Variables (Production) aggiungi le 4 env (i nomi esatti letti dal codice):

  • GCP_VERTEX_PROJECT, GCP_VERTEX_LOCATION, GCP_SA_CLIENT_EMAIL = i valori stampati;
  • GCP_SA_PRIVATE_KEY = il valore del campo "private_key" dal JSON (incolla così com'è, con i \n: il codice li normalizza). Mai incollare la chiave in chat.

Fai Redeploy (Vercel lo propone al cambio env). Da quel momento il testo del Pianeta Content gira reale e sovrano, e Imagen (immagini) è disponibile con le stesse credenziali. Sicurezza: la chiave la gestisci tu in Vercel, io non la vedo mai (regola "segreti mai in chat").

Imagen usa le stesse credenziali (nessuna env extra). Veo (video) resta OFF: è geo-bloccato in CH e Pre-GA non commerciale (vedi roadmap) → il video userà la via DACH-safe quando la costruiamo.

Modo manuale (Console, se preferisci cliccare)

  1. GCP project: usa o crea un progetto Google Cloud (es. multiverbe-prod).
  2. Abilita l'API: Vertex AI API (aiplatform.googleapis.com).
  3. Regione EU: scegli europe-west1 (Belgio) o europe-west4 (Olanda). Usa la stessa ovunque.
  4. Service account: creane uno (es. vertex-runtime@…), ruolo roles/aiplatform.user.
  5. Chiave JSON: crea una key JSON e scaricala (NON committarla).
  6. Claude su Vertex (per il routing premium Business): in Vertex Model Garden abilita i modelli Anthropic per la regione (accetta i termini) e annota l'id esatto del modello.
  7. Veo / Imagen: abilitali nella stessa regione (stesse credenziali, nessuna env extra).
  8. Env su Vercel (project app):
    • GCP_VERTEX_PROJECT = id progetto
    • GCP_VERTEX_LOCATION = europe-west1 (o europe-west4)
    • GCP_SA_CLIENT_EMAIL = client_email dal JSON
    • GCP_SA_PRIVATE_KEY = private_key dal JSON (lascia i \n come sono, il codice li normalizza)
    • (opz.) GCP_VERTEX_CLAUDE_MODEL = id modello Claude su Vertex (default claude-sonnet-4-6)
  9. DPA: firma il Google Cloud Data Processing Addendum + conferma data residency EU e no-training.

→ Quando A è fatto: il testo del Pianeta Content gira reale e sovrano (e l'outreach se aggiungi B2).

B. Provider sovrani aggiuntivi

B1 · Mistral FR (outreach, già quasi pronto) · sblocca L2 in prod

  • MISTRAL_API_KEY (probabilmente già presente).
  • Account Mistral: abilita Zero Data Retention + firma il DPA Mistral.

B2 · Immagini via Azure Foundry FLUX (alternativa a Imagen) · SOV2 + SOV4

  • Crea una risorsa Azure AI Foundry in regione EU, deploya FLUX.
  • AZURE_FOUNDRY_ENDPOINT, AZURE_FOUNDRY_KEY.
  • SOV4: apri un ticket Microsoft per forzare EU Data Boundary sulla risorsa (default è "global").
  • (Se preferisci, salta B2 e usa Imagen via Vertex EU dal punto A — meno fornitori da gestire.)

B3 · Voce · sblocca voce + C9

  • AZURE_SPEECH_ENDPOINT, AZURE_SPEECH_KEY (TTS neurale EU/CH).
  • ELEVENLABS_API_KEY (solo cloning premium, dietro consenso voice_clone_consent).
  • C9: A/B reale qualità voce IT (Voxtral/Azure vs ElevenLabs) prima di scegliere il default.

C. Legale / DPA · SOV5

  • Firma DPA con: Google Cloud, Mistral, (se usi B2) Microsoft Azure, (se userai il fallback economico) fal.ai.
  • Versiona la lista sub-processor accettata per-workspace (tabella legal_acceptances, document_type sub_processor_list, già sbloccato dalla migration 2026-06-02a).

D. Database · applicare in ordine (Dashboard SQL, NON MCP)

  1. database/migrations/2026-06-02a_content_compliance.sql (L4 + L5 + registro sovranità + attestazioni Art.50)
  2. database/migrations/2026-06-02b_content_engine.sql (concept layer + jobs + review_log/ai_disclosure)
  3. database/migrations/2026-06-02c_content_quota_ring.sql (cap testo/media + ring)
  4. database/migrations/2026-06-02d_idempotency_keys.sql (idempotency · non urgente, gli endpoint degradano fail-open senza)

Nota: l'endpoint di firma editoriale api/content/approve.ts (esonero AI Act Art.50) resta in 503 finché a+b non sono applicate.

Ogni file ha un pre-check in cima: eseguilo prima (deve dare zero righe in conflitto).

E. Solo DOPO A–D testati · SOV7

Sblocca il claim marketing "i tuoi dati restano in Europa / sei già a norma". Prima di allora il claim è pubblicità potenzialmente ingannevole (art. 3 LCSl) + responsabilità verso il cliente. Tienilo spento finché il routing EU non è verificato in prod.

F. Decisioni residue (non bloccanti per il codice)

  • C9: scelta default voce dopo A/B (sopra B3).
  • L8: ToS — riscrivere la clausola indennizzi ("ti giriamo le garanzie del fornitore ove esistenti e nei loro limiti, senza garanzia di Multiverbe"). Far passare da legale + humanizer.
  • Adozione LIA cold outreach: ops/lia-cold-outreach-2026-06-02.md (firma + data nel decisions-log).