@emilia-protocol/gate 0.9.5 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -0
- package/LICENSE +190 -0
- package/README.md +95 -16
- package/action-control-manifest.js +26 -0
- package/action-escrow-custodian.js +395 -0
- package/action-escrow-evidence.js +859 -0
- package/action-escrow-package.js +1669 -0
- package/action-escrow-postgres.js +338 -0
- package/action-escrow-state.js +397 -0
- package/action-escrow-verifiers.js +332 -0
- package/action-escrow.js +2448 -0
- package/adapters/_kit.js +47 -5
- package/adapters/aws.js +15 -4
- package/adapters/github-demo.mjs +25 -22
- package/adapters/github.js +1 -1
- package/adapters/jira.js +1 -0
- package/adapters/linear.js +1 -0
- package/adapters/salesforce.js +1 -0
- package/adapters/stripe.js +1 -1
- package/adapters/supabase.js +26 -4
- package/adapters/vercel.js +33 -4
- package/aec-execution.js +1 -3
- package/breakglass.js +285 -51
- package/control-plane.js +341 -0
- package/coverage.js +722 -0
- package/custody-demo.mjs +13 -3
- package/demo.mjs +9 -3
- package/deploy/helm/README.md +16 -8
- package/deploy/helm/emilia-gate/Chart.yaml +3 -1
- package/deploy/helm/emilia-gate/templates/NOTES.txt +3 -2
- package/deploy/helm/emilia-gate/templates/deployment.yaml +55 -1
- package/deploy/helm/emilia-gate/values.yaml +18 -2
- package/deploy/helm/emilia-gate-service/Chart.yaml +11 -0
- package/deploy/helm/emilia-gate-service/README.md +81 -0
- package/deploy/helm/emilia-gate-service/templates/NOTES.txt +12 -0
- package/deploy/helm/emilia-gate-service/templates/_helpers.tpl +83 -0
- package/deploy/helm/emilia-gate-service/templates/deployment.yaml +153 -0
- package/deploy/helm/emilia-gate-service/templates/migration-job.yaml +73 -0
- package/deploy/helm/emilia-gate-service/templates/networkpolicy.yaml +113 -0
- package/deploy/helm/emilia-gate-service/templates/pdb.yaml +14 -0
- package/deploy/helm/emilia-gate-service/templates/service.yaml +20 -0
- package/deploy/helm/emilia-gate-service/templates/serviceaccount.yaml +8 -0
- package/deploy/helm/emilia-gate-service/tests/fixtures/001_gate.sql +26 -0
- package/deploy/helm/emilia-gate-service/tests/fixtures/002_runtime_access.sql +32 -0
- package/deploy/helm/emilia-gate-service/tests/fixtures/gate.config.mjs +29 -0
- package/deploy/helm/emilia-gate-service/tests/render-check.sh +145 -0
- package/deploy/helm/emilia-gate-service/values.schema.json +145 -0
- package/deploy/helm/emilia-gate-service/values.yaml +166 -0
- package/deploy/sql/001-runtime.sql +707 -0
- package/deploy/terraform/README.md +24 -14
- package/deploy/terraform/main.tf +59 -0
- package/deploy/terraform/service/README.md +81 -0
- package/deploy/terraform/service/main.tf +718 -0
- package/deploy/terraform/service/outputs.tf +19 -0
- package/deploy/terraform/service/tests/validate.sh +24 -0
- package/deploy/terraform/service/tests/validation.tftest.hcl +168 -0
- package/deploy/terraform/service/variables.tf +459 -0
- package/deploy/terraform/service/versions.tf +10 -0
- package/deploy/terraform/variables.tf +95 -2
- package/deployment-attestation.js +248 -0
- package/eg1-conformance.js +46 -12
- package/enterprise.js +6 -2
- package/ep-assure.mjs +3 -2
- package/evidence-postgres.js +357 -0
- package/evidence.js +10 -3
- package/execution-binding.js +141 -26
- package/index.js +556 -115
- package/key-registry.js +51 -19
- package/mcp.js +4 -2
- package/network-witness.js +500 -0
- package/package.json +39 -5
- package/reliance-kernel.js +5 -6
- package/reliance-packet.js +43 -10
- package/reports/assurance-package.js +45 -19
- package/reports/reperform.js +78 -18
- package/reports/underwriter.js +1 -1
- package/settlement.js +300 -0
- package/store-postgres.js +14 -0
- package/store.js +26 -5
- package/strict-json.js +103 -0
- package/witness-postgres.js +97 -0
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
-- SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
-- EMILIA Gate Postgres evidence runtime (EP-GATE-PG-EVIDENCE-v1).
|
|
3
|
+
--
|
|
4
|
+
-- Run this migration as a non-runtime owner with CREATE EXTENSION, CREATEROLE,
|
|
5
|
+
-- and CREATE privileges. Grant emilia_gate_evidence_runtime to each login used
|
|
6
|
+
-- by Gate, then bind each login to exact evidence and binary witness stream
|
|
7
|
+
-- scopes with grant_runtime_scope() and grant_network_witness_scope(). Runtime
|
|
8
|
+
-- reads are RLS-filtered and each write function authorizes session_user.
|
|
9
|
+
|
|
10
|
+
BEGIN;
|
|
11
|
+
|
|
12
|
+
CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public;
|
|
13
|
+
|
|
14
|
+
DO $role$
|
|
15
|
+
BEGIN
|
|
16
|
+
IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = 'emilia_gate_evidence_runtime') THEN
|
|
17
|
+
CREATE ROLE emilia_gate_evidence_runtime
|
|
18
|
+
NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION;
|
|
19
|
+
END IF;
|
|
20
|
+
END
|
|
21
|
+
$role$;
|
|
22
|
+
|
|
23
|
+
DO $role_safety$
|
|
24
|
+
BEGIN
|
|
25
|
+
IF EXISTS (
|
|
26
|
+
SELECT 1
|
|
27
|
+
FROM pg_catalog.pg_roles
|
|
28
|
+
WHERE rolname = 'emilia_gate_evidence_runtime'
|
|
29
|
+
AND (
|
|
30
|
+
rolcanlogin OR rolsuper OR rolcreatedb OR rolcreaterole
|
|
31
|
+
OR rolreplication OR rolbypassrls
|
|
32
|
+
)
|
|
33
|
+
) THEN
|
|
34
|
+
RAISE EXCEPTION 'emilia_gate_evidence_runtime has unsafe privileged attributes'
|
|
35
|
+
USING ERRCODE = '42501';
|
|
36
|
+
END IF;
|
|
37
|
+
END
|
|
38
|
+
$role_safety$;
|
|
39
|
+
|
|
40
|
+
ALTER ROLE emilia_gate_evidence_runtime NOLOGIN NOINHERIT;
|
|
41
|
+
|
|
42
|
+
CREATE SCHEMA IF NOT EXISTS emilia_gate_evidence;
|
|
43
|
+
REVOKE ALL ON SCHEMA emilia_gate_evidence FROM PUBLIC;
|
|
44
|
+
REVOKE CREATE ON SCHEMA emilia_gate_evidence FROM emilia_gate_evidence_runtime;
|
|
45
|
+
GRANT USAGE ON SCHEMA emilia_gate_evidence TO emilia_gate_evidence_runtime;
|
|
46
|
+
|
|
47
|
+
CREATE TABLE IF NOT EXISTS emilia_gate_evidence.heads (
|
|
48
|
+
tenant_id TEXT NOT NULL CHECK (char_length(tenant_id) BETWEEN 1 AND 256),
|
|
49
|
+
gate_id TEXT NOT NULL CHECK (char_length(gate_id) BETWEEN 1 AND 256),
|
|
50
|
+
stream_id TEXT NOT NULL CHECK (char_length(stream_id) BETWEEN 1 AND 256),
|
|
51
|
+
head_seq BIGINT NOT NULL DEFAULT -1,
|
|
52
|
+
head_hash TEXT,
|
|
53
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
|
|
54
|
+
PRIMARY KEY (tenant_id, gate_id, stream_id),
|
|
55
|
+
CONSTRAINT evidence_head_shape CHECK (
|
|
56
|
+
(head_seq = -1 AND head_hash IS NULL)
|
|
57
|
+
OR (head_seq BETWEEN 0 AND 9007199254740991 AND head_hash ~ '^[0-9a-f]{64}$')
|
|
58
|
+
)
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
CREATE TABLE IF NOT EXISTS emilia_gate_evidence.records (
|
|
62
|
+
tenant_id TEXT NOT NULL CHECK (char_length(tenant_id) BETWEEN 1 AND 256),
|
|
63
|
+
gate_id TEXT NOT NULL CHECK (char_length(gate_id) BETWEEN 1 AND 256),
|
|
64
|
+
stream_id TEXT NOT NULL CHECK (char_length(stream_id) BETWEEN 1 AND 256),
|
|
65
|
+
seq BIGINT NOT NULL CHECK (seq BETWEEN 0 AND 9007199254740991),
|
|
66
|
+
record_id TEXT NOT NULL CHECK (char_length(record_id) BETWEEN 16 AND 256),
|
|
67
|
+
prev_hash TEXT NOT NULL CHECK (prev_hash = 'genesis' OR prev_hash ~ '^[0-9a-f]{64}$'),
|
|
68
|
+
hash TEXT NOT NULL CHECK (hash ~ '^[0-9a-f]{64}$'),
|
|
69
|
+
record JSONB NOT NULL CHECK (jsonb_typeof(record) = 'object'),
|
|
70
|
+
recorded_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
|
|
71
|
+
PRIMARY KEY (tenant_id, gate_id, stream_id, seq),
|
|
72
|
+
UNIQUE (tenant_id, gate_id, stream_id, record_id),
|
|
73
|
+
UNIQUE (tenant_id, gate_id, stream_id, hash),
|
|
74
|
+
FOREIGN KEY (tenant_id, gate_id, stream_id)
|
|
75
|
+
REFERENCES emilia_gate_evidence.heads (tenant_id, gate_id, stream_id)
|
|
76
|
+
ON DELETE RESTRICT,
|
|
77
|
+
CONSTRAINT evidence_record_seq_match CHECK (
|
|
78
|
+
jsonb_typeof(record -> 'seq') = 'number'
|
|
79
|
+
AND (record ->> 'seq')::numeric = seq
|
|
80
|
+
),
|
|
81
|
+
CONSTRAINT evidence_record_id_match CHECK (record ->> 'record_id' = record_id),
|
|
82
|
+
CONSTRAINT evidence_record_prev_match CHECK (record ->> 'prev_hash' = prev_hash),
|
|
83
|
+
CONSTRAINT evidence_record_hash_match CHECK (record ->> 'hash' = hash)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
CREATE TABLE IF NOT EXISTS emilia_gate_evidence.runtime_scope_grants (
|
|
87
|
+
login_role NAME NOT NULL,
|
|
88
|
+
tenant_id TEXT NOT NULL CHECK (char_length(tenant_id) BETWEEN 1 AND 256),
|
|
89
|
+
gate_id TEXT NOT NULL CHECK (char_length(gate_id) BETWEEN 1 AND 256),
|
|
90
|
+
stream_id TEXT NOT NULL CHECK (char_length(stream_id) BETWEEN 1 AND 256),
|
|
91
|
+
granted_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
|
|
92
|
+
PRIMARY KEY (login_role, tenant_id, gate_id, stream_id)
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
CREATE TABLE IF NOT EXISTS emilia_gate_evidence.network_witness_scope_grants (
|
|
96
|
+
login_role NAME NOT NULL,
|
|
97
|
+
tenant_id TEXT NOT NULL CHECK (char_length(tenant_id) BETWEEN 1 AND 256),
|
|
98
|
+
gate_id TEXT NOT NULL CHECK (char_length(gate_id) BETWEEN 1 AND 256),
|
|
99
|
+
stream_key BYTEA NOT NULL CHECK (octet_length(stream_key) BETWEEN 1 AND 1024),
|
|
100
|
+
granted_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
|
|
101
|
+
PRIMARY KEY (login_role, tenant_id, gate_id, stream_key)
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
CREATE TABLE IF NOT EXISTS emilia_gate_evidence.network_witness_checkpoints (
|
|
105
|
+
tenant_id TEXT NOT NULL CHECK (char_length(tenant_id) BETWEEN 1 AND 256),
|
|
106
|
+
gate_id TEXT NOT NULL CHECK (char_length(gate_id) BETWEEN 1 AND 256),
|
|
107
|
+
stream_key BYTEA NOT NULL CHECK (octet_length(stream_key) BETWEEN 1 AND 1024),
|
|
108
|
+
sequence BIGINT NOT NULL CHECK (sequence BETWEEN 0 AND 9007199254740991),
|
|
109
|
+
statement_digest TEXT NOT NULL CHECK (statement_digest ~ '^sha256:[0-9a-f]{64}$'),
|
|
110
|
+
equivocated BOOLEAN NOT NULL DEFAULT FALSE,
|
|
111
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
|
|
112
|
+
PRIMARY KEY (tenant_id, gate_id, stream_key)
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
ALTER TABLE emilia_gate_evidence.network_witness_checkpoints
|
|
116
|
+
ADD COLUMN IF NOT EXISTS equivocated BOOLEAN NOT NULL DEFAULT FALSE;
|
|
117
|
+
|
|
118
|
+
-- Re-running the migration cannot leave the runtime role as an object owner;
|
|
119
|
+
-- ownership bypasses ordinary table ACLs in Postgres.
|
|
120
|
+
ALTER SCHEMA emilia_gate_evidence OWNER TO CURRENT_USER;
|
|
121
|
+
ALTER TABLE emilia_gate_evidence.heads OWNER TO CURRENT_USER;
|
|
122
|
+
ALTER TABLE emilia_gate_evidence.records OWNER TO CURRENT_USER;
|
|
123
|
+
ALTER TABLE emilia_gate_evidence.runtime_scope_grants OWNER TO CURRENT_USER;
|
|
124
|
+
ALTER TABLE emilia_gate_evidence.network_witness_scope_grants OWNER TO CURRENT_USER;
|
|
125
|
+
ALTER TABLE emilia_gate_evidence.network_witness_checkpoints OWNER TO CURRENT_USER;
|
|
126
|
+
|
|
127
|
+
CREATE OR REPLACE FUNCTION emilia_gate_evidence.reject_record_mutation()
|
|
128
|
+
RETURNS trigger
|
|
129
|
+
LANGUAGE plpgsql
|
|
130
|
+
SET search_path = pg_catalog, pg_temp
|
|
131
|
+
AS $function$
|
|
132
|
+
BEGIN
|
|
133
|
+
RAISE EXCEPTION 'EMILIA evidence records are append-only'
|
|
134
|
+
USING ERRCODE = '55000';
|
|
135
|
+
END
|
|
136
|
+
$function$;
|
|
137
|
+
|
|
138
|
+
ALTER FUNCTION emilia_gate_evidence.reject_record_mutation() OWNER TO CURRENT_USER;
|
|
139
|
+
|
|
140
|
+
DROP TRIGGER IF EXISTS evidence_records_are_immutable ON emilia_gate_evidence.records;
|
|
141
|
+
CREATE TRIGGER evidence_records_are_immutable
|
|
142
|
+
BEFORE UPDATE OR DELETE OR TRUNCATE ON emilia_gate_evidence.records
|
|
143
|
+
FOR EACH STATEMENT EXECUTE FUNCTION emilia_gate_evidence.reject_record_mutation();
|
|
144
|
+
|
|
145
|
+
CREATE OR REPLACE FUNCTION emilia_gate_evidence.runtime_scope_authorized(
|
|
146
|
+
p_tenant_id TEXT,
|
|
147
|
+
p_gate_id TEXT,
|
|
148
|
+
p_stream_id TEXT
|
|
149
|
+
)
|
|
150
|
+
RETURNS BOOLEAN
|
|
151
|
+
LANGUAGE sql
|
|
152
|
+
STABLE
|
|
153
|
+
SECURITY DEFINER
|
|
154
|
+
SET search_path = pg_catalog, pg_temp
|
|
155
|
+
AS $function$
|
|
156
|
+
SELECT EXISTS (
|
|
157
|
+
SELECT 1
|
|
158
|
+
FROM emilia_gate_evidence.runtime_scope_grants AS g
|
|
159
|
+
JOIN pg_catalog.pg_roles AS r ON r.rolname = g.login_role
|
|
160
|
+
WHERE g.login_role = session_user
|
|
161
|
+
AND g.tenant_id = p_tenant_id
|
|
162
|
+
AND g.gate_id = p_gate_id
|
|
163
|
+
AND g.stream_id = p_stream_id
|
|
164
|
+
AND r.rolcanlogin
|
|
165
|
+
AND NOT r.rolsuper
|
|
166
|
+
AND NOT r.rolbypassrls
|
|
167
|
+
AND pg_catalog.pg_has_role(
|
|
168
|
+
session_user,
|
|
169
|
+
'emilia_gate_evidence_runtime',
|
|
170
|
+
'MEMBER'
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
$function$;
|
|
174
|
+
|
|
175
|
+
CREATE OR REPLACE FUNCTION emilia_gate_evidence.grant_runtime_scope(
|
|
176
|
+
p_login_role NAME,
|
|
177
|
+
p_tenant_id TEXT,
|
|
178
|
+
p_gate_id TEXT,
|
|
179
|
+
p_stream_id TEXT
|
|
180
|
+
)
|
|
181
|
+
RETURNS VOID
|
|
182
|
+
LANGUAGE plpgsql
|
|
183
|
+
SECURITY DEFINER
|
|
184
|
+
SET search_path = pg_catalog, pg_temp
|
|
185
|
+
AS $function$
|
|
186
|
+
DECLARE
|
|
187
|
+
v_role pg_catalog.pg_roles%ROWTYPE;
|
|
188
|
+
BEGIN
|
|
189
|
+
IF p_tenant_id IS NULL OR char_length(p_tenant_id) NOT BETWEEN 1 AND 256
|
|
190
|
+
OR p_gate_id IS NULL OR char_length(p_gate_id) NOT BETWEEN 1 AND 256
|
|
191
|
+
OR p_stream_id IS NULL OR char_length(p_stream_id) NOT BETWEEN 1 AND 256 THEN
|
|
192
|
+
RAISE EXCEPTION 'invalid EMILIA evidence scope' USING ERRCODE = '22023';
|
|
193
|
+
END IF;
|
|
194
|
+
|
|
195
|
+
SELECT * INTO v_role
|
|
196
|
+
FROM pg_catalog.pg_roles
|
|
197
|
+
WHERE rolname = p_login_role;
|
|
198
|
+
IF NOT FOUND OR NOT v_role.rolcanlogin THEN
|
|
199
|
+
RAISE EXCEPTION 'EMILIA evidence scope requires an existing login role'
|
|
200
|
+
USING ERRCODE = '22023';
|
|
201
|
+
END IF;
|
|
202
|
+
IF v_role.rolsuper OR v_role.rolbypassrls THEN
|
|
203
|
+
RAISE EXCEPTION 'EMILIA runtime login must not bypass row security'
|
|
204
|
+
USING ERRCODE = '42501';
|
|
205
|
+
END IF;
|
|
206
|
+
IF NOT pg_catalog.pg_has_role(
|
|
207
|
+
p_login_role,
|
|
208
|
+
'emilia_gate_evidence_runtime',
|
|
209
|
+
'MEMBER'
|
|
210
|
+
) THEN
|
|
211
|
+
RAISE EXCEPTION 'EMILIA runtime login must be a member of emilia_gate_evidence_runtime'
|
|
212
|
+
USING ERRCODE = '42501';
|
|
213
|
+
END IF;
|
|
214
|
+
|
|
215
|
+
INSERT INTO emilia_gate_evidence.runtime_scope_grants (
|
|
216
|
+
login_role, tenant_id, gate_id, stream_id
|
|
217
|
+
) VALUES (
|
|
218
|
+
p_login_role, p_tenant_id, p_gate_id, p_stream_id
|
|
219
|
+
) ON CONFLICT DO NOTHING;
|
|
220
|
+
END
|
|
221
|
+
$function$;
|
|
222
|
+
|
|
223
|
+
CREATE OR REPLACE FUNCTION emilia_gate_evidence.revoke_runtime_scope(
|
|
224
|
+
p_login_role NAME,
|
|
225
|
+
p_tenant_id TEXT,
|
|
226
|
+
p_gate_id TEXT,
|
|
227
|
+
p_stream_id TEXT
|
|
228
|
+
)
|
|
229
|
+
RETURNS BOOLEAN
|
|
230
|
+
LANGUAGE sql
|
|
231
|
+
SECURITY DEFINER
|
|
232
|
+
SET search_path = pg_catalog, pg_temp
|
|
233
|
+
AS $function$
|
|
234
|
+
WITH deleted AS (
|
|
235
|
+
DELETE FROM emilia_gate_evidence.runtime_scope_grants
|
|
236
|
+
WHERE login_role = p_login_role
|
|
237
|
+
AND tenant_id = p_tenant_id
|
|
238
|
+
AND gate_id = p_gate_id
|
|
239
|
+
AND stream_id = p_stream_id
|
|
240
|
+
RETURNING 1
|
|
241
|
+
)
|
|
242
|
+
SELECT EXISTS (SELECT 1 FROM deleted)
|
|
243
|
+
$function$;
|
|
244
|
+
|
|
245
|
+
CREATE OR REPLACE FUNCTION emilia_gate_evidence.network_witness_scope_authorized(
|
|
246
|
+
p_tenant_id TEXT,
|
|
247
|
+
p_gate_id TEXT,
|
|
248
|
+
p_stream_key BYTEA
|
|
249
|
+
)
|
|
250
|
+
RETURNS BOOLEAN
|
|
251
|
+
LANGUAGE sql
|
|
252
|
+
STABLE
|
|
253
|
+
SECURITY DEFINER
|
|
254
|
+
SET search_path = pg_catalog, pg_temp
|
|
255
|
+
AS $function$
|
|
256
|
+
SELECT EXISTS (
|
|
257
|
+
SELECT 1
|
|
258
|
+
FROM emilia_gate_evidence.network_witness_scope_grants AS g
|
|
259
|
+
JOIN pg_catalog.pg_roles AS r ON r.rolname = g.login_role
|
|
260
|
+
WHERE g.login_role = session_user
|
|
261
|
+
AND g.tenant_id = p_tenant_id
|
|
262
|
+
AND g.gate_id = p_gate_id
|
|
263
|
+
AND g.stream_key = p_stream_key
|
|
264
|
+
AND r.rolcanlogin
|
|
265
|
+
AND NOT r.rolsuper
|
|
266
|
+
AND NOT r.rolbypassrls
|
|
267
|
+
AND pg_catalog.pg_has_role(
|
|
268
|
+
session_user,
|
|
269
|
+
'emilia_gate_evidence_runtime',
|
|
270
|
+
'MEMBER'
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
$function$;
|
|
274
|
+
|
|
275
|
+
CREATE OR REPLACE FUNCTION emilia_gate_evidence.grant_network_witness_scope(
|
|
276
|
+
p_login_role NAME,
|
|
277
|
+
p_tenant_id TEXT,
|
|
278
|
+
p_gate_id TEXT,
|
|
279
|
+
p_stream_key BYTEA
|
|
280
|
+
)
|
|
281
|
+
RETURNS VOID
|
|
282
|
+
LANGUAGE plpgsql
|
|
283
|
+
SECURITY DEFINER
|
|
284
|
+
SET search_path = pg_catalog, pg_temp
|
|
285
|
+
AS $function$
|
|
286
|
+
DECLARE
|
|
287
|
+
v_role pg_catalog.pg_roles%ROWTYPE;
|
|
288
|
+
BEGIN
|
|
289
|
+
IF p_tenant_id IS NULL OR char_length(p_tenant_id) NOT BETWEEN 1 AND 256
|
|
290
|
+
OR p_gate_id IS NULL OR char_length(p_gate_id) NOT BETWEEN 1 AND 256
|
|
291
|
+
OR p_stream_key IS NULL OR octet_length(p_stream_key) NOT BETWEEN 1 AND 1024 THEN
|
|
292
|
+
RAISE EXCEPTION 'invalid EMILIA network-witness scope' USING ERRCODE = '22023';
|
|
293
|
+
END IF;
|
|
294
|
+
|
|
295
|
+
SELECT * INTO v_role
|
|
296
|
+
FROM pg_catalog.pg_roles
|
|
297
|
+
WHERE rolname = p_login_role;
|
|
298
|
+
IF NOT FOUND OR NOT v_role.rolcanlogin THEN
|
|
299
|
+
RAISE EXCEPTION 'EMILIA network-witness scope requires an existing login role'
|
|
300
|
+
USING ERRCODE = '22023';
|
|
301
|
+
END IF;
|
|
302
|
+
IF v_role.rolsuper OR v_role.rolbypassrls THEN
|
|
303
|
+
RAISE EXCEPTION 'EMILIA runtime login must not bypass row security'
|
|
304
|
+
USING ERRCODE = '42501';
|
|
305
|
+
END IF;
|
|
306
|
+
IF NOT pg_catalog.pg_has_role(
|
|
307
|
+
p_login_role,
|
|
308
|
+
'emilia_gate_evidence_runtime',
|
|
309
|
+
'MEMBER'
|
|
310
|
+
) THEN
|
|
311
|
+
RAISE EXCEPTION 'EMILIA runtime login must be a member of emilia_gate_evidence_runtime'
|
|
312
|
+
USING ERRCODE = '42501';
|
|
313
|
+
END IF;
|
|
314
|
+
|
|
315
|
+
INSERT INTO emilia_gate_evidence.network_witness_scope_grants (
|
|
316
|
+
login_role, tenant_id, gate_id, stream_key
|
|
317
|
+
) VALUES (
|
|
318
|
+
p_login_role, p_tenant_id, p_gate_id, p_stream_key
|
|
319
|
+
) ON CONFLICT DO NOTHING;
|
|
320
|
+
END
|
|
321
|
+
$function$;
|
|
322
|
+
|
|
323
|
+
CREATE OR REPLACE FUNCTION emilia_gate_evidence.revoke_network_witness_scope(
|
|
324
|
+
p_login_role NAME,
|
|
325
|
+
p_tenant_id TEXT,
|
|
326
|
+
p_gate_id TEXT,
|
|
327
|
+
p_stream_key BYTEA
|
|
328
|
+
)
|
|
329
|
+
RETURNS BOOLEAN
|
|
330
|
+
LANGUAGE sql
|
|
331
|
+
SECURITY DEFINER
|
|
332
|
+
SET search_path = pg_catalog, pg_temp
|
|
333
|
+
AS $function$
|
|
334
|
+
WITH deleted AS (
|
|
335
|
+
DELETE FROM emilia_gate_evidence.network_witness_scope_grants
|
|
336
|
+
WHERE login_role = p_login_role
|
|
337
|
+
AND tenant_id = p_tenant_id
|
|
338
|
+
AND gate_id = p_gate_id
|
|
339
|
+
AND stream_key = p_stream_key
|
|
340
|
+
RETURNING 1
|
|
341
|
+
)
|
|
342
|
+
SELECT EXISTS (SELECT 1 FROM deleted)
|
|
343
|
+
$function$;
|
|
344
|
+
|
|
345
|
+
CREATE OR REPLACE FUNCTION emilia_gate_evidence.advance_network_witness_checkpoint(
|
|
346
|
+
p_tenant_id TEXT,
|
|
347
|
+
p_gate_id TEXT,
|
|
348
|
+
p_stream_key BYTEA,
|
|
349
|
+
p_sequence BIGINT,
|
|
350
|
+
p_statement_digest TEXT
|
|
351
|
+
)
|
|
352
|
+
RETURNS TABLE (accepted BOOLEAN, reason TEXT)
|
|
353
|
+
LANGUAGE plpgsql
|
|
354
|
+
SECURITY DEFINER
|
|
355
|
+
SET search_path = pg_catalog, pg_temp
|
|
356
|
+
AS $function$
|
|
357
|
+
DECLARE
|
|
358
|
+
v_current_digest TEXT;
|
|
359
|
+
v_current_sequence BIGINT;
|
|
360
|
+
v_equivocated BOOLEAN;
|
|
361
|
+
v_inserted INTEGER;
|
|
362
|
+
BEGIN
|
|
363
|
+
IF p_tenant_id IS NULL OR char_length(p_tenant_id) NOT BETWEEN 1 AND 256
|
|
364
|
+
OR p_gate_id IS NULL OR char_length(p_gate_id) NOT BETWEEN 1 AND 256
|
|
365
|
+
OR p_stream_key IS NULL OR octet_length(p_stream_key) NOT BETWEEN 1 AND 1024 THEN
|
|
366
|
+
RAISE EXCEPTION 'invalid EMILIA network-witness scope' USING ERRCODE = '22023';
|
|
367
|
+
END IF;
|
|
368
|
+
IF NOT emilia_gate_evidence.network_witness_scope_authorized(
|
|
369
|
+
p_tenant_id,
|
|
370
|
+
p_gate_id,
|
|
371
|
+
p_stream_key
|
|
372
|
+
) THEN
|
|
373
|
+
RAISE EXCEPTION 'EMILIA network-witness scope is not authorized for session login %', session_user
|
|
374
|
+
USING ERRCODE = '42501';
|
|
375
|
+
END IF;
|
|
376
|
+
IF p_sequence IS NULL OR p_sequence < 0 OR p_sequence > 9007199254740991 THEN
|
|
377
|
+
RAISE EXCEPTION 'invalid EMILIA network-witness sequence' USING ERRCODE = '22003';
|
|
378
|
+
END IF;
|
|
379
|
+
IF p_statement_digest IS NULL
|
|
380
|
+
OR p_statement_digest !~ '^sha256:[0-9a-f]{64}$' THEN
|
|
381
|
+
RAISE EXCEPTION 'invalid EMILIA network-witness statement digest'
|
|
382
|
+
USING ERRCODE = '22023';
|
|
383
|
+
END IF;
|
|
384
|
+
|
|
385
|
+
INSERT INTO emilia_gate_evidence.network_witness_checkpoints (
|
|
386
|
+
tenant_id, gate_id, stream_key, sequence, statement_digest
|
|
387
|
+
) VALUES (
|
|
388
|
+
p_tenant_id, p_gate_id, p_stream_key, p_sequence, p_statement_digest
|
|
389
|
+
) ON CONFLICT (tenant_id, gate_id, stream_key) DO NOTHING;
|
|
390
|
+
GET DIAGNOSTICS v_inserted = ROW_COUNT;
|
|
391
|
+
IF v_inserted = 1 THEN
|
|
392
|
+
RETURN QUERY SELECT TRUE, NULL::TEXT;
|
|
393
|
+
RETURN;
|
|
394
|
+
END IF;
|
|
395
|
+
|
|
396
|
+
SELECT checkpoint.sequence, checkpoint.statement_digest, checkpoint.equivocated
|
|
397
|
+
INTO STRICT v_current_sequence, v_current_digest, v_equivocated
|
|
398
|
+
FROM emilia_gate_evidence.network_witness_checkpoints AS checkpoint
|
|
399
|
+
WHERE checkpoint.tenant_id = p_tenant_id
|
|
400
|
+
AND checkpoint.gate_id = p_gate_id
|
|
401
|
+
AND checkpoint.stream_key = p_stream_key
|
|
402
|
+
FOR UPDATE;
|
|
403
|
+
|
|
404
|
+
IF v_equivocated THEN
|
|
405
|
+
RETURN QUERY SELECT FALSE, 'sequence_equivocation'::TEXT;
|
|
406
|
+
RETURN;
|
|
407
|
+
END IF;
|
|
408
|
+
|
|
409
|
+
IF p_sequence < v_current_sequence THEN
|
|
410
|
+
RETURN QUERY SELECT FALSE, 'sequence_rollback'::TEXT;
|
|
411
|
+
RETURN;
|
|
412
|
+
END IF;
|
|
413
|
+
IF p_sequence = v_current_sequence THEN
|
|
414
|
+
IF p_statement_digest = v_current_digest THEN
|
|
415
|
+
RETURN QUERY SELECT FALSE, 'statement_replay'::TEXT;
|
|
416
|
+
ELSE
|
|
417
|
+
UPDATE emilia_gate_evidence.network_witness_checkpoints
|
|
418
|
+
SET equivocated = TRUE,
|
|
419
|
+
updated_at = clock_timestamp()
|
|
420
|
+
WHERE tenant_id = p_tenant_id
|
|
421
|
+
AND gate_id = p_gate_id
|
|
422
|
+
AND stream_key = p_stream_key
|
|
423
|
+
AND sequence = v_current_sequence
|
|
424
|
+
AND statement_digest = v_current_digest
|
|
425
|
+
AND equivocated = FALSE;
|
|
426
|
+
IF NOT FOUND THEN
|
|
427
|
+
RAISE EXCEPTION 'EMILIA network-witness equivocation fence was lost'
|
|
428
|
+
USING ERRCODE = '40001';
|
|
429
|
+
END IF;
|
|
430
|
+
RETURN QUERY SELECT FALSE, 'sequence_equivocation'::TEXT;
|
|
431
|
+
END IF;
|
|
432
|
+
RETURN;
|
|
433
|
+
END IF;
|
|
434
|
+
|
|
435
|
+
UPDATE emilia_gate_evidence.network_witness_checkpoints
|
|
436
|
+
SET sequence = p_sequence,
|
|
437
|
+
statement_digest = p_statement_digest,
|
|
438
|
+
updated_at = clock_timestamp()
|
|
439
|
+
WHERE tenant_id = p_tenant_id
|
|
440
|
+
AND gate_id = p_gate_id
|
|
441
|
+
AND stream_key = p_stream_key
|
|
442
|
+
AND sequence = v_current_sequence
|
|
443
|
+
AND statement_digest = v_current_digest
|
|
444
|
+
AND equivocated = FALSE;
|
|
445
|
+
IF NOT FOUND THEN
|
|
446
|
+
RAISE EXCEPTION 'EMILIA network-witness checkpoint fence was lost'
|
|
447
|
+
USING ERRCODE = '40001';
|
|
448
|
+
END IF;
|
|
449
|
+
|
|
450
|
+
RETURN QUERY SELECT TRUE, NULL::TEXT;
|
|
451
|
+
END
|
|
452
|
+
$function$;
|
|
453
|
+
|
|
454
|
+
ALTER FUNCTION emilia_gate_evidence.runtime_scope_authorized(TEXT, TEXT, TEXT)
|
|
455
|
+
OWNER TO CURRENT_USER;
|
|
456
|
+
ALTER FUNCTION emilia_gate_evidence.grant_runtime_scope(NAME, TEXT, TEXT, TEXT)
|
|
457
|
+
OWNER TO CURRENT_USER;
|
|
458
|
+
ALTER FUNCTION emilia_gate_evidence.revoke_runtime_scope(NAME, TEXT, TEXT, TEXT)
|
|
459
|
+
OWNER TO CURRENT_USER;
|
|
460
|
+
ALTER FUNCTION emilia_gate_evidence.network_witness_scope_authorized(TEXT, TEXT, BYTEA)
|
|
461
|
+
OWNER TO CURRENT_USER;
|
|
462
|
+
ALTER FUNCTION emilia_gate_evidence.grant_network_witness_scope(NAME, TEXT, TEXT, BYTEA)
|
|
463
|
+
OWNER TO CURRENT_USER;
|
|
464
|
+
ALTER FUNCTION emilia_gate_evidence.revoke_network_witness_scope(NAME, TEXT, TEXT, BYTEA)
|
|
465
|
+
OWNER TO CURRENT_USER;
|
|
466
|
+
ALTER FUNCTION emilia_gate_evidence.advance_network_witness_checkpoint(TEXT, TEXT, BYTEA, BIGINT, TEXT)
|
|
467
|
+
OWNER TO CURRENT_USER;
|
|
468
|
+
|
|
469
|
+
CREATE OR REPLACE FUNCTION emilia_gate_evidence.append_record(
|
|
470
|
+
p_tenant_id TEXT,
|
|
471
|
+
p_gate_id TEXT,
|
|
472
|
+
p_stream_id TEXT,
|
|
473
|
+
p_expected_head_hash TEXT,
|
|
474
|
+
p_record JSONB,
|
|
475
|
+
p_canonical_body TEXT
|
|
476
|
+
)
|
|
477
|
+
RETURNS BOOLEAN
|
|
478
|
+
LANGUAGE plpgsql
|
|
479
|
+
SECURITY DEFINER
|
|
480
|
+
SET search_path = pg_catalog, pg_temp
|
|
481
|
+
AS $function$
|
|
482
|
+
DECLARE
|
|
483
|
+
v_body JSONB;
|
|
484
|
+
v_head_hash TEXT;
|
|
485
|
+
v_head_seq BIGINT;
|
|
486
|
+
v_hash TEXT;
|
|
487
|
+
v_prev_hash TEXT;
|
|
488
|
+
v_record_id TEXT;
|
|
489
|
+
v_seq BIGINT;
|
|
490
|
+
v_updated INTEGER;
|
|
491
|
+
BEGIN
|
|
492
|
+
IF p_tenant_id IS NULL OR char_length(p_tenant_id) NOT BETWEEN 1 AND 256
|
|
493
|
+
OR p_gate_id IS NULL OR char_length(p_gate_id) NOT BETWEEN 1 AND 256
|
|
494
|
+
OR p_stream_id IS NULL OR char_length(p_stream_id) NOT BETWEEN 1 AND 256 THEN
|
|
495
|
+
RAISE EXCEPTION 'invalid EMILIA evidence scope' USING ERRCODE = '22023';
|
|
496
|
+
END IF;
|
|
497
|
+
IF NOT emilia_gate_evidence.runtime_scope_authorized(
|
|
498
|
+
p_tenant_id,
|
|
499
|
+
p_gate_id,
|
|
500
|
+
p_stream_id
|
|
501
|
+
) THEN
|
|
502
|
+
RAISE EXCEPTION 'EMILIA evidence scope is not authorized for session login %', session_user
|
|
503
|
+
USING ERRCODE = '42501';
|
|
504
|
+
END IF;
|
|
505
|
+
IF p_expected_head_hash IS NOT NULL AND p_expected_head_hash !~ '^[0-9a-f]{64}$' THEN
|
|
506
|
+
RAISE EXCEPTION 'invalid expected EMILIA evidence head' USING ERRCODE = '22023';
|
|
507
|
+
END IF;
|
|
508
|
+
IF p_record IS NULL OR jsonb_typeof(p_record) <> 'object'
|
|
509
|
+
OR p_canonical_body IS NULL
|
|
510
|
+
OR octet_length(p_canonical_body) > 8388608
|
|
511
|
+
OR jsonb_typeof(p_record -> 'seq') <> 'number'
|
|
512
|
+
OR coalesce(p_record ->> 'seq', '') !~ '^(0|[1-9][0-9]*)$'
|
|
513
|
+
OR char_length(coalesce(p_record ->> 'record_id', '')) NOT BETWEEN 16 AND 256
|
|
514
|
+
OR coalesce(p_record ->> 'prev_hash', '') !~ '^(genesis|[0-9a-f]{64})$'
|
|
515
|
+
OR coalesce(p_record ->> 'hash', '') !~ '^[0-9a-f]{64}$' THEN
|
|
516
|
+
RAISE EXCEPTION 'invalid EMILIA evidence record' USING ERRCODE = '22023';
|
|
517
|
+
END IF;
|
|
518
|
+
|
|
519
|
+
BEGIN
|
|
520
|
+
v_body := p_canonical_body::jsonb;
|
|
521
|
+
v_seq := (p_record ->> 'seq')::bigint;
|
|
522
|
+
EXCEPTION WHEN OTHERS THEN
|
|
523
|
+
RAISE EXCEPTION 'invalid EMILIA canonical evidence body' USING ERRCODE = '22023';
|
|
524
|
+
END;
|
|
525
|
+
IF v_seq < 0 OR v_seq > 9007199254740991 THEN
|
|
526
|
+
RAISE EXCEPTION 'EMILIA evidence sequence is outside the safe-integer range'
|
|
527
|
+
USING ERRCODE = '22003';
|
|
528
|
+
END IF;
|
|
529
|
+
|
|
530
|
+
v_record_id := p_record ->> 'record_id';
|
|
531
|
+
v_prev_hash := p_record ->> 'prev_hash';
|
|
532
|
+
v_hash := p_record ->> 'hash';
|
|
533
|
+
IF v_body IS DISTINCT FROM (p_record - 'hash')
|
|
534
|
+
OR encode(public.digest(convert_to(p_canonical_body, 'UTF8'), 'sha256'), 'hex')
|
|
535
|
+
IS DISTINCT FROM v_hash THEN
|
|
536
|
+
RAISE EXCEPTION 'EMILIA evidence canonical hash mismatch' USING ERRCODE = '23514';
|
|
537
|
+
END IF;
|
|
538
|
+
|
|
539
|
+
INSERT INTO emilia_gate_evidence.heads (tenant_id, gate_id, stream_id)
|
|
540
|
+
VALUES (p_tenant_id, p_gate_id, p_stream_id)
|
|
541
|
+
ON CONFLICT (tenant_id, gate_id, stream_id) DO NOTHING;
|
|
542
|
+
|
|
543
|
+
SELECT h.head_seq, h.head_hash
|
|
544
|
+
INTO STRICT v_head_seq, v_head_hash
|
|
545
|
+
FROM emilia_gate_evidence.heads h
|
|
546
|
+
WHERE h.tenant_id = p_tenant_id
|
|
547
|
+
AND h.gate_id = p_gate_id
|
|
548
|
+
AND h.stream_id = p_stream_id
|
|
549
|
+
FOR UPDATE;
|
|
550
|
+
|
|
551
|
+
-- A false return means ordinary optimistic contention. Every malformed or
|
|
552
|
+
-- indeterminate condition raises, so callers cannot mistake it for a retry.
|
|
553
|
+
IF p_expected_head_hash IS DISTINCT FROM v_head_hash THEN
|
|
554
|
+
RETURN FALSE;
|
|
555
|
+
END IF;
|
|
556
|
+
IF v_seq <> v_head_seq + 1
|
|
557
|
+
OR v_prev_hash <> coalesce(v_head_hash, 'genesis') THEN
|
|
558
|
+
RAISE EXCEPTION 'EMILIA evidence record does not extend the locked head'
|
|
559
|
+
USING ERRCODE = '23514';
|
|
560
|
+
END IF;
|
|
561
|
+
|
|
562
|
+
INSERT INTO emilia_gate_evidence.records (
|
|
563
|
+
tenant_id, gate_id, stream_id, seq, record_id, prev_hash, hash, record
|
|
564
|
+
) VALUES (
|
|
565
|
+
p_tenant_id, p_gate_id, p_stream_id, v_seq, v_record_id, v_prev_hash, v_hash, p_record
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
UPDATE emilia_gate_evidence.heads
|
|
569
|
+
SET head_seq = v_seq, head_hash = v_hash, updated_at = clock_timestamp()
|
|
570
|
+
WHERE tenant_id = p_tenant_id
|
|
571
|
+
AND gate_id = p_gate_id
|
|
572
|
+
AND stream_id = p_stream_id
|
|
573
|
+
AND head_seq = v_head_seq
|
|
574
|
+
AND head_hash IS NOT DISTINCT FROM v_head_hash;
|
|
575
|
+
GET DIAGNOSTICS v_updated = ROW_COUNT;
|
|
576
|
+
IF v_updated <> 1 THEN
|
|
577
|
+
RAISE EXCEPTION 'EMILIA evidence head fence was lost' USING ERRCODE = '40001';
|
|
578
|
+
END IF;
|
|
579
|
+
|
|
580
|
+
RETURN TRUE;
|
|
581
|
+
END
|
|
582
|
+
$function$;
|
|
583
|
+
|
|
584
|
+
ALTER FUNCTION emilia_gate_evidence.append_record(TEXT, TEXT, TEXT, TEXT, JSONB, TEXT)
|
|
585
|
+
OWNER TO CURRENT_USER;
|
|
586
|
+
|
|
587
|
+
ALTER TABLE emilia_gate_evidence.heads ENABLE ROW LEVEL SECURITY;
|
|
588
|
+
ALTER TABLE emilia_gate_evidence.heads FORCE ROW LEVEL SECURITY;
|
|
589
|
+
ALTER TABLE emilia_gate_evidence.records ENABLE ROW LEVEL SECURITY;
|
|
590
|
+
ALTER TABLE emilia_gate_evidence.records FORCE ROW LEVEL SECURITY;
|
|
591
|
+
ALTER TABLE emilia_gate_evidence.network_witness_checkpoints ENABLE ROW LEVEL SECURITY;
|
|
592
|
+
ALTER TABLE emilia_gate_evidence.network_witness_checkpoints FORCE ROW LEVEL SECURITY;
|
|
593
|
+
|
|
594
|
+
DROP POLICY IF EXISTS evidence_heads_runtime_read ON emilia_gate_evidence.heads;
|
|
595
|
+
CREATE POLICY evidence_heads_runtime_read
|
|
596
|
+
ON emilia_gate_evidence.heads
|
|
597
|
+
FOR SELECT
|
|
598
|
+
TO emilia_gate_evidence_runtime
|
|
599
|
+
USING (emilia_gate_evidence.runtime_scope_authorized(tenant_id, gate_id, stream_id));
|
|
600
|
+
|
|
601
|
+
DROP POLICY IF EXISTS evidence_records_runtime_read ON emilia_gate_evidence.records;
|
|
602
|
+
CREATE POLICY evidence_records_runtime_read
|
|
603
|
+
ON emilia_gate_evidence.records
|
|
604
|
+
FOR SELECT
|
|
605
|
+
TO emilia_gate_evidence_runtime
|
|
606
|
+
USING (emilia_gate_evidence.runtime_scope_authorized(tenant_id, gate_id, stream_id));
|
|
607
|
+
|
|
608
|
+
DROP POLICY IF EXISTS network_witness_runtime_read
|
|
609
|
+
ON emilia_gate_evidence.network_witness_checkpoints;
|
|
610
|
+
CREATE POLICY network_witness_runtime_read
|
|
611
|
+
ON emilia_gate_evidence.network_witness_checkpoints
|
|
612
|
+
FOR SELECT
|
|
613
|
+
TO emilia_gate_evidence_runtime
|
|
614
|
+
USING (emilia_gate_evidence.network_witness_scope_authorized(tenant_id, gate_id, stream_key));
|
|
615
|
+
|
|
616
|
+
-- FORCE RLS applies to the table owner too. Give only the current migration
|
|
617
|
+
-- owner an all-rows policy so SECURITY DEFINER append can perform its fenced
|
|
618
|
+
-- write after validating session_user. Re-runs replace stale owner policies.
|
|
619
|
+
DROP POLICY IF EXISTS evidence_heads_owner_all ON emilia_gate_evidence.heads;
|
|
620
|
+
DROP POLICY IF EXISTS evidence_records_owner_all ON emilia_gate_evidence.records;
|
|
621
|
+
DROP POLICY IF EXISTS network_witness_owner_all
|
|
622
|
+
ON emilia_gate_evidence.network_witness_checkpoints;
|
|
623
|
+
DO $policies$
|
|
624
|
+
BEGIN
|
|
625
|
+
EXECUTE format(
|
|
626
|
+
'CREATE POLICY evidence_heads_owner_all ON emilia_gate_evidence.heads FOR ALL TO %I USING (true) WITH CHECK (true)',
|
|
627
|
+
current_user
|
|
628
|
+
);
|
|
629
|
+
EXECUTE format(
|
|
630
|
+
'CREATE POLICY evidence_records_owner_all ON emilia_gate_evidence.records FOR ALL TO %I USING (true) WITH CHECK (true)',
|
|
631
|
+
current_user
|
|
632
|
+
);
|
|
633
|
+
EXECUTE format(
|
|
634
|
+
'CREATE POLICY network_witness_owner_all ON emilia_gate_evidence.network_witness_checkpoints FOR ALL TO %I USING (true) WITH CHECK (true)',
|
|
635
|
+
current_user
|
|
636
|
+
);
|
|
637
|
+
END
|
|
638
|
+
$policies$;
|
|
639
|
+
|
|
640
|
+
REVOKE ALL ON TABLE
|
|
641
|
+
emilia_gate_evidence.records,
|
|
642
|
+
emilia_gate_evidence.heads,
|
|
643
|
+
emilia_gate_evidence.runtime_scope_grants,
|
|
644
|
+
emilia_gate_evidence.network_witness_scope_grants,
|
|
645
|
+
emilia_gate_evidence.network_witness_checkpoints
|
|
646
|
+
FROM PUBLIC;
|
|
647
|
+
REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER
|
|
648
|
+
ON emilia_gate_evidence.records, emilia_gate_evidence.heads
|
|
649
|
+
FROM emilia_gate_evidence_runtime;
|
|
650
|
+
REVOKE ALL ON TABLE emilia_gate_evidence.runtime_scope_grants
|
|
651
|
+
FROM emilia_gate_evidence_runtime;
|
|
652
|
+
REVOKE ALL ON TABLE emilia_gate_evidence.network_witness_scope_grants
|
|
653
|
+
FROM emilia_gate_evidence_runtime;
|
|
654
|
+
REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER
|
|
655
|
+
ON emilia_gate_evidence.network_witness_checkpoints
|
|
656
|
+
FROM emilia_gate_evidence_runtime;
|
|
657
|
+
GRANT SELECT ON emilia_gate_evidence.records, emilia_gate_evidence.heads
|
|
658
|
+
TO emilia_gate_evidence_runtime;
|
|
659
|
+
GRANT SELECT ON emilia_gate_evidence.network_witness_checkpoints
|
|
660
|
+
TO emilia_gate_evidence_runtime;
|
|
661
|
+
|
|
662
|
+
REVOKE ALL ON FUNCTION emilia_gate_evidence.reject_record_mutation() FROM PUBLIC;
|
|
663
|
+
REVOKE ALL ON FUNCTION emilia_gate_evidence.reject_record_mutation()
|
|
664
|
+
FROM emilia_gate_evidence_runtime;
|
|
665
|
+
REVOKE ALL ON FUNCTION emilia_gate_evidence.runtime_scope_authorized(TEXT, TEXT, TEXT)
|
|
666
|
+
FROM PUBLIC, emilia_gate_evidence_runtime;
|
|
667
|
+
GRANT EXECUTE ON FUNCTION emilia_gate_evidence.runtime_scope_authorized(TEXT, TEXT, TEXT)
|
|
668
|
+
TO emilia_gate_evidence_runtime;
|
|
669
|
+
REVOKE ALL ON FUNCTION emilia_gate_evidence.grant_runtime_scope(NAME, TEXT, TEXT, TEXT)
|
|
670
|
+
FROM PUBLIC, emilia_gate_evidence_runtime;
|
|
671
|
+
REVOKE ALL ON FUNCTION emilia_gate_evidence.revoke_runtime_scope(NAME, TEXT, TEXT, TEXT)
|
|
672
|
+
FROM PUBLIC, emilia_gate_evidence_runtime;
|
|
673
|
+
REVOKE ALL ON FUNCTION emilia_gate_evidence.network_witness_scope_authorized(TEXT, TEXT, BYTEA)
|
|
674
|
+
FROM PUBLIC, emilia_gate_evidence_runtime;
|
|
675
|
+
GRANT EXECUTE ON FUNCTION emilia_gate_evidence.network_witness_scope_authorized(TEXT, TEXT, BYTEA)
|
|
676
|
+
TO emilia_gate_evidence_runtime;
|
|
677
|
+
REVOKE ALL ON FUNCTION emilia_gate_evidence.grant_network_witness_scope(NAME, TEXT, TEXT, BYTEA)
|
|
678
|
+
FROM PUBLIC, emilia_gate_evidence_runtime;
|
|
679
|
+
REVOKE ALL ON FUNCTION emilia_gate_evidence.revoke_network_witness_scope(NAME, TEXT, TEXT, BYTEA)
|
|
680
|
+
FROM PUBLIC, emilia_gate_evidence_runtime;
|
|
681
|
+
REVOKE ALL ON FUNCTION emilia_gate_evidence.advance_network_witness_checkpoint(TEXT, TEXT, BYTEA, BIGINT, TEXT)
|
|
682
|
+
FROM PUBLIC, emilia_gate_evidence_runtime;
|
|
683
|
+
GRANT EXECUTE ON FUNCTION emilia_gate_evidence.advance_network_witness_checkpoint(TEXT, TEXT, BYTEA, BIGINT, TEXT)
|
|
684
|
+
TO emilia_gate_evidence_runtime;
|
|
685
|
+
REVOKE ALL ON FUNCTION emilia_gate_evidence.append_record(TEXT, TEXT, TEXT, TEXT, JSONB, TEXT)
|
|
686
|
+
FROM PUBLIC, emilia_gate_evidence_runtime;
|
|
687
|
+
GRANT EXECUTE ON FUNCTION emilia_gate_evidence.append_record(TEXT, TEXT, TEXT, TEXT, JSONB, TEXT)
|
|
688
|
+
TO emilia_gate_evidence_runtime;
|
|
689
|
+
|
|
690
|
+
COMMENT ON FUNCTION emilia_gate_evidence.append_record(TEXT, TEXT, TEXT, TEXT, JSONB, TEXT)
|
|
691
|
+
IS 'Authorize session_user, then atomically compare-and-append canonical EMILIA evidence under a scoped head row lock.';
|
|
692
|
+
COMMENT ON FUNCTION emilia_gate_evidence.grant_runtime_scope(NAME, TEXT, TEXT, TEXT)
|
|
693
|
+
IS 'Bind one non-bypass runtime login to one exact tenant/gate/stream evidence scope.';
|
|
694
|
+
COMMENT ON FUNCTION emilia_gate_evidence.grant_network_witness_scope(NAME, TEXT, TEXT, BYTEA)
|
|
695
|
+
IS 'Bind one non-bypass runtime login to one exact tenant/gate/binary witness stream scope.';
|
|
696
|
+
COMMENT ON TABLE emilia_gate_evidence.runtime_scope_grants
|
|
697
|
+
IS 'Owner-managed bindings from runtime login roles to exact textual evidence scopes.';
|
|
698
|
+
COMMENT ON TABLE emilia_gate_evidence.network_witness_scope_grants
|
|
699
|
+
IS 'Owner-managed bindings from runtime login roles to exact binary network-witness stream keys.';
|
|
700
|
+
COMMENT ON FUNCTION emilia_gate_evidence.advance_network_witness_checkpoint(TEXT, TEXT, BYTEA, BIGINT, TEXT)
|
|
701
|
+
IS 'Atomically advance a scoped witness checkpoint or distinguish replay, rollback, and same-sequence equivocation.';
|
|
702
|
+
COMMENT ON TABLE emilia_gate_evidence.network_witness_checkpoints
|
|
703
|
+
IS 'Latest tenant/gate/stream network-witness sequence and statement digest; a checkpoint, not complete observation history.';
|
|
704
|
+
COMMENT ON TABLE emilia_gate_evidence.records
|
|
705
|
+
IS 'Immutable tenant/gate/stream-scoped EMILIA evidence records.';
|
|
706
|
+
|
|
707
|
+
COMMIT;
|