Interne, peut changer sans préavis. Ce que décrit cette page relève de
l’exploitation d’un déploiement, pas du contrat que la passerelle offre à un
programme tiers. L’éditeur ne s’y engage pas : ces noms peuvent changer d’une
version à l’autre, sans étape de dépréciation. Un changement se manifeste alors
au démarrage ou à la mise à jour, par un message qui nomme ce qui manque.
La partie sur laquelle l’éditeur s’engage est le
relais.
La passerelle et la console d’administration lisent la même base. Cette page décrit son
schéma tel que le fichier d’installation le crée, plus la table de registre que le script de
migration ajoute pour son propre compte.
Le schéma ne porte presque aucun commentaire lisible par une machine. Cette page liste
donc les colonnes, leur type, leur caractère obligatoire et leur valeur par défaut, sans
expliquer à quoi chacune sert. Le jour où le schéma sera annoté, les descriptions
apparaîtront ici sans que personne n’ait à réécrire cette page.
Une base neuve ne contient aucun fournisseur, aucun modèle, aucun point d’appel et aucun
compte : l’exploitant les déclare depuis la console d’administration.
Un responsable de la protection des données cherche d’abord celles-ci. Le classement est
fait sur le nom de la colonne, ce qui le rend vrai pour les colonnes qui seront ajoutées
plus tard sans que personne y pense.
| Colonne | Nature |
|---|
users.username | donnée personnelle |
access_keys.key_hash | secret |
access_keys.key_hint | secret |
authorization_decisions.subject | donnée personnelle |
identity_sessions.subject | donnée personnelle |
subject_roles.subject | donnée personnelle |
subject_roles.granted_by | donnée personnelle |
authorization_policies.installed_by | donnée personnelle |
subject_revocations.subject | donnée personnelle |
subject_revocations.revoked_by | donnée personnelle |
subject_revocations.lifted_by | donnée personnelle |
administration_actions.actor | donnée personnelle |
administration_actions.target | donnée personnelle |
agent_acts.subject | donnée personnelle |
agent_acts.target | donnée personnelle |
agent_acts.decided_by | donnée personnelle |
agent_session_revocations.revoked_by | donnée personnelle |
organization_policies.installed_by | donnée personnelle |
organization_policy_journal.actor | donnée personnelle |
relay_usage.subject | donnée personnelle |
Aucune clé d’API n’est stockée en clair : la base garde un condensat et le fragment que la
console affiche pour reconnaître une clé sans la relire.
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | SERIAL | oui | aucun | clé primaire |
name | TEXT | oui | aucun | unique |
max_cost_euro | NUMERIC(10,4) | non | aucun | |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | SERIAL | oui | aucun | clé primaire |
username | TEXT | oui | aucun | unique, donnée personnelle |
active | BOOLEAN | oui | TRUE | |
created_at | TIMESTAMPTZ | oui | NOW() | |
plan_id | INTEGER | non | aucun | référence plans(id) |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | BIGSERIAL | oui | aucun | clé primaire |
user_id | INTEGER | oui | aucun | référence users(id) |
key_hash | TEXT | oui | aucun | unique, secret |
key_hint | TEXT | oui | aucun | secret |
name | TEXT | oui | aucun | |
created_at | TIMESTAMPTZ | oui | NOW() | |
last_used_at | TIMESTAMPTZ | non | aucun | |
revoked_at | TIMESTAMPTZ | non | aucun | |
Contraintes
| Nom | Règle |
|---|
access_keys_name_not_blank | CHECK (btrim(name) <> '') |
Index
| Nom | Unique | Colonnes | Restreint à |
|---|
access_keys_by_account | non | user_id | aucune restriction |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | SERIAL | oui | aucun | clé primaire |
slug | TEXT | oui | aucun | unique |
name | TEXT | oui | aucun | |
base_url | TEXT | oui | aucun | |
api_key_env | TEXT | non | aucun | |
api_key_header | TEXT | non | aucun | |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | SERIAL | oui | aucun | clé primaire |
slug | TEXT | oui | aucun | unique |
name | TEXT | oui | aucun | |
provider_id | INTEGER | oui | aucun | référence providers(id) |
input_token_price_nano_euro | BIGINT | oui | 0 | |
output_token_price_nano_euro | BIGINT | oui | 0 | |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | SERIAL | oui | aucun | clé primaire |
name | TEXT | oui | aucun | unique |
model_id | INTEGER | oui | aucun | référence models(id) |
enabled | BOOLEAN | oui | TRUE | |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | SERIAL | oui | aucun | clé primaire |
timestamp | TIMESTAMPTZ | oui | NOW() | |
user_id | INTEGER | non | aucun | référence users(id) |
endpoint_id | INTEGER | non | aucun | référence endpoints(id) |
model | TEXT | oui | aucun | |
latency_ms | INTEGER | oui | aucun | |
input_tokens | INTEGER | non | aucun | |
output_tokens | INTEGER | non | aucun | |
request_id | UUID | non | aucun | |
Index
| Nom | Unique | Colonnes | Restreint à |
|---|
request_logs_request_id_unique | oui | request_id | request_id IS NOT NULL |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | BIGSERIAL | oui | aucun | clé primaire |
decided_at | TIMESTAMPTZ | oui | clock_timestamp() | |
subject | TEXT | non | aucun | donnée personnelle |
identity_source | TEXT | oui | aucun | |
resource | TEXT | oui | aucun | |
decision | TEXT | oui | aucun | |
reason | TEXT | non | aucun | |
request_id | TEXT | non | aucun | |
Contraintes
| Nom | Règle |
|---|
authorization_decisions_decision_connue | CHECK (decision IN ('accordee', 'refusee')) |
authorization_decisions_source_connue | CHECK (identity_source IN ('annuaire', 'locale', 'inconnue')) |
authorization_decisions_accord_nomme_son_sujet | CHECK (decision <> 'accordee' OR subject IS NOT NULL) |
Index
| Nom | Unique | Colonnes | Restreint à |
|---|
authorization_decisions_sujet_instant | non | subject, decided_at DESC | aucune restriction |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | TEXT | oui | aucun | clé primaire |
subject | TEXT | oui | aucun | donnée personnelle |
identity_source | TEXT | oui | aucun | |
opened_at | TIMESTAMPTZ | oui | clock_timestamp() | |
expires_at | TIMESTAMPTZ | oui | aucun | |
ended_at | TIMESTAMPTZ | non | aucun | |
ended_reason | TEXT | non | aucun | |
Contraintes
| Nom | Règle |
|---|
identity_sessions_source_connue | CHECK (identity_source IN ('annuaire', 'locale')) |
identity_sessions_fin_motivee | CHECK ((ended_at IS NULL) = (ended_reason IS NULL)) |
Index
| Nom | Unique | Colonnes | Restreint à |
|---|
identity_sessions_sujet_vivantes | non | subject | ended_at IS NULL |
| Colonne | Type | Obligatoire | Défaut | |
|---|
credits_per_eur | INTEGER | oui | aucun | |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | SERIAL | oui | aucun | clé primaire |
subject | TEXT | oui | aucun | donnée personnelle |
role | TEXT | oui | aucun | |
scope | TEXT | non | aucun | |
pole | TEXT | non | aucun | calculée |
granted_at | TIMESTAMPTZ | oui | NOW() | |
granted_by | TEXT | oui | aucun | donnée personnelle |
Contraintes
| Nom | Règle |
|---|
subject_roles_known_role | CHECK ( role IN ( 'utilisateur-standard', 'administrateur-de-projet', 'administrateur-d-instance', 'auditeur' ) ) |
subject_roles_scope_iff_project | CHECK ( (role = 'administrateur-de-projet') = (scope IS NOT NULL) ) |
subject_roles_attribution_unique | UNIQUE NULLS NOT DISTINCT (subject, role, scope) |
Index
| Nom | Unique | Colonnes | Restreint à |
|---|
subject_roles_by_subject | non | subject | aucune restriction |
Déclencheurs
| Nom | Quand | Effet |
|---|
subject_roles_cumul_interdit | BEFORE INSERT OR UPDATE | FOR EACH ROW EXECUTE FUNCTION refuse_administration_audit_overlap() |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | BIGSERIAL | oui | aucun | clé primaire |
installed_at | TIMESTAMPTZ | oui | clock_timestamp() | |
installed_by | TEXT | oui | aucun | donnée personnelle |
document | JSONB | oui | aucun | |
name | TEXT | non | aucun | calculée |
Contraintes
| Nom | Règle |
|---|
authorization_policies_document_est_un_objet | CHECK (jsonb_typeof(document) = 'object') |
authorization_policies_auteur_non_vide | CHECK (btrim(installed_by) <> '') |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | BIGSERIAL | oui | aucun | clé primaire |
subject | TEXT | oui | aucun | donnée personnelle |
revoked_at | TIMESTAMPTZ | oui | clock_timestamp() | |
revoked_by | TEXT | oui | aucun | donnée personnelle |
reason | TEXT | oui | aucun | |
lifted_at | TIMESTAMPTZ | non | aucun | |
lifted_by | TEXT | non | aucun | donnée personnelle |
Contraintes
| Nom | Règle |
|---|
subject_revocations_auteur_non_vide | CHECK (btrim(revoked_by) <> '') |
subject_revocations_motif_non_vide | CHECK (btrim(reason) <> '') |
subject_revocations_sujet_non_vide | CHECK (btrim(subject) <> '') |
subject_revocations_levee_endossee | CHECK ((lifted_at IS NULL) = (lifted_by IS NULL)) |
subject_revocations_leveur_non_vide | CHECK (lifted_by IS NULL OR btrim(lifted_by) <> '') |
Index
| Nom | Unique | Colonnes | Restreint à |
|---|
subject_revocations_une_seule_en_vigueur | oui | subject | lifted_at IS NULL |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | BIGSERIAL | oui | aucun | clé primaire |
performed_at | TIMESTAMPTZ | oui | clock_timestamp() | |
action | TEXT | oui | aucun | |
actor | TEXT | oui | aucun | donnée personnelle |
actor_assurance | TEXT | oui | aucun | |
target | TEXT | non | aucun | donnée personnelle |
role | TEXT | non | aucun | |
scope | TEXT | non | aucun | |
outcome | TEXT | oui | aucun | |
refusal_reason | TEXT | non | aucun | |
policy_id | BIGINT | non | aucun | |
Contraintes
| Nom | Règle |
|---|
administration_actions_acte_connu | CHECK (action IN ( 'attribution-de-role', 'retrait-de-role', 'revocation-de-sujet', 'levee-de-revocation', 'installation-de-politique', 'installation-de-politique-d-organisation', 'revocation-de-session-d-agent' )) |
administration_actions_issue_connue | CHECK (outcome IN ('effectue', 'sans-effet', 'refuse')) |
administration_actions_assurance_connue | CHECK (actor_assurance IN ('declaree', 'etablie')) |
administration_actions_acteur_non_vide | CHECK (btrim(actor) <> '') |
administration_actions_cible_nommee | CHECK ( action NOT IN ( 'attribution-de-role', 'retrait-de-role', 'revocation-de-sujet', 'levee-de-revocation', 'revocation-de-session-d-agent' ) OR (target IS NOT NULL AND btrim(target) <> '') ) |
administration_actions_role_ssi_acte_de_role | CHECK ( (action IN ('attribution-de-role', 'retrait-de-role')) = (role IS NOT NULL) ) |
administration_actions_perimetre_suppose_un_role | CHECK (scope IS NULL OR role IS NOT NULL) |
administration_actions_politique_ssi_installation | CHECK ( ( policy_id IS NULL OR action IN ( 'installation-de-politique', 'installation-de-politique-d-organisation' ) ) AND ( action NOT IN ( 'installation-de-politique', 'installation-de-politique-d-organisation' ) OR outcome <> 'effectue' OR policy_id IS NOT NULL ) ) |
administration_actions_refus_motive | CHECK ((outcome = 'refuse') = (refusal_reason IS NOT NULL)) |
Index
| Nom | Unique | Colonnes | Restreint à |
|---|
administration_actions_cible_instant | non | target, performed_at DESC | aucune restriction |
administration_actions_acteur_instant | non | actor, performed_at DESC | aucune restriction |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | BIGSERIAL | oui | aucun | clé primaire |
announced_at | TIMESTAMPTZ | oui | clock_timestamp() | |
launcher | TEXT | non | aucun | |
guarantee_held | BOOLEAN | oui | aucun | |
reservations | JSONB | oui | aucun | |
Contraintes
| Nom | Règle |
|---|
agent_execution_announcements_reserves_ssi_non_tenue | CHECK (guarantee_held = (jsonb_array_length(reservations) = 0)) |
agent_execution_announcements_reserves_en_tableau | CHECK (jsonb_typeof(reservations) = 'array') |
agent_execution_announcements_sans_lanceur_sans_garantie | CHECK (launcher IS NOT NULL OR guarantee_held = FALSE) |
Index
| Nom | Unique | Colonnes | Restreint à |
|---|
agent_execution_announcements_instant | non | announced_at DESC | aucune restriction |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | BIGSERIAL | oui | aucun | clé primaire |
received_at | TIMESTAMPTZ | oui | clock_timestamp() | |
session | TEXT | oui | aucun | |
workstation | TEXT | non | aucun | |
subject | TEXT | non | aucun | donnée personnelle |
subject_assurance | TEXT | oui | 'declaree' | |
surface | TEXT | oui | aucun | |
agent | TEXT | oui | aucun | |
at | TIMESTAMPTZ | oui | aucun | |
rank | INTEGER | oui | aucun | |
kind | TEXT | oui | aucun | |
tool | TEXT | non | aucun | |
target | TEXT | non | aucun | donnée personnelle |
decision | TEXT | non | aucun | |
decided_by | TEXT | non | aucun | donnée personnelle |
outcome | TEXT | non | aucun | |
reason | TEXT | non | aucun | |
duration_ms | INTEGER | non | aucun | |
mode | TEXT | non | aucun | |
rule_count | INTEGER | non | aucun | |
tools | JSONB | non | aucun | |
Contraintes
| Nom | Règle |
|---|
agent_acts_assurance_connue | CHECK (subject_assurance IN ('declaree', 'etablie')) |
agent_acts_surface_connue | CHECK (surface IN ('cli', 'ide')) |
agent_acts_rang_positif | CHECK (rank >= 1) |
agent_acts_genre_connu | CHECK (kind IN ('tool-call', 'session-frame')) |
agent_acts_decision_connue | CHECK (decision IS NULL OR decision IN ('allowed', 'refused')) |
agent_acts_decideur_connu | CHECK (decided_by IS NULL OR decided_by IN ('human', 'automation')) |
agent_acts_issue_connue | CHECK (outcome IS NULL OR outcome IN ('succeeded', 'failed', 'not-executed')) |
agent_acts_motif_borne | CHECK (reason IS NULL OR char_length(reason) <= 300) |
agent_acts_outils_en_tableau | CHECK (tools IS NULL OR jsonb_typeof(tools) = 'array') |
agent_acts_appel_complet | CHECK (kind <> 'tool-call' OR (tool IS NOT NULL AND decision IS NOT NULL AND decided_by IS NOT NULL AND outcome IS NOT NULL)) |
agent_acts_cadre_complet | CHECK (kind <> 'session-frame' OR mode IS NOT NULL) |
agent_acts_un_rang_par_session | UNIQUE (session, rank) |
Index
| Nom | Unique | Colonnes | Restreint à |
|---|
agent_acts_session_rang | non | session, rank | aucune restriction |
agent_acts_instant | non | received_at DESC | aucune restriction |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | BIGSERIAL | oui | aucun | clé primaire |
session | TEXT | oui | aucun | |
revoked_at | TIMESTAMPTZ | oui | clock_timestamp() | |
revoked_by | TEXT | oui | aucun | donnée personnelle |
revoked_by_assurance | TEXT | oui | aucun | |
reason | TEXT | oui | aucun | |
Contraintes
| Nom | Règle |
|---|
agent_session_revocations_session_non_vide | CHECK (btrim(session) <> '') |
agent_session_revocations_auteur_non_vide | CHECK (btrim(revoked_by) <> '') |
agent_session_revocations_assurance_connue | CHECK (revoked_by_assurance IN ('declaree', 'etablie')) |
agent_session_revocations_motif_non_vide | CHECK (btrim(reason) <> '') |
agent_session_revocations_une_par_session | UNIQUE (session) |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | BIGSERIAL | oui | aucun | clé primaire |
slug | TEXT | oui | aucun | unique |
name | TEXT | oui | aucun | |
created_at | TIMESTAMPTZ | oui | now() | |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | BIGSERIAL | oui | aucun | clé primaire |
organization_id | BIGINT | oui | aucun | référence organizations(id) |
revision | TEXT | oui | aucun | |
document | JSONB | oui | aucun | |
validity_seconds | INTEGER | oui | aucun | |
installed_at | TIMESTAMPTZ | oui | now() | |
installed_by | TEXT | oui | aucun | donnée personnelle |
Contraintes
| Nom | Règle |
|---|
| “ | UNIQUE (organization_id, revision) |
Index
| Nom | Unique | Colonnes | Restreint à |
|---|
organization_policies_in_force | non | organization_id, id DESC | aucune restriction |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | BIGSERIAL | oui | aucun | clé primaire |
organization_id | BIGINT | non | aucun | référence organizations(id) |
at | TIMESTAMPTZ | oui | now() | |
actor | TEXT | oui | aucun | donnée personnelle |
actor_assurance | TEXT | oui | aucun | |
outcome | TEXT | oui | aucun | |
revision | TEXT | non | aucun | |
refusal_reason | TEXT | non | aucun | |
document | JSONB | non | aucun | |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | BIGSERIAL | oui | aucun | clé primaire |
announced_at | TIMESTAMPTZ | oui | clock_timestamp() | |
served_organization | TEXT | oui | aucun | |
| Colonne | Type | Obligatoire | Défaut | |
|---|
id | BIGSERIAL | oui | aucun | clé primaire |
recorded_at | TIMESTAMPTZ | oui | clock_timestamp() | |
subject | TEXT | oui | aucun | donnée personnelle |
identity_source | TEXT | oui | aucun | |
model | TEXT | oui | aucun | |
input_tokens | INTEGER | non | aucun | |
output_tokens | INTEGER | non | aucun | |
latency_ms | INTEGER | non | aucun | |
request_id | TEXT | non | aucun | |
Index
| Nom | Unique | Colonnes | Restreint à |
|---|
relay_usage_sujet_instant | non | subject, recorded_at DESC | aucune restriction |
Créée par gateway-db/migrate.sh, et non par le fichier d’installation.
| Colonne | Type | Obligatoire | Défaut | |
|---|
version | TEXT | oui | aucun | clé primaire |
applied_at | TIMESTAMPTZ | oui | NOW() | |
Les migrations elles-mêmes, l’ordre dans lequel elles s’appliquent, la sauvegarde et la
restauration ne sont pas décrites ici. Ce qui est décrit est l’état du schéma, pas le
chemin par lequel une base y arrive.