-module(lightspeed@ops@tenant_harness).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/lightspeed/ops/tenant_harness.gleam").
-export([run_scenario/1, run_matrix/0, scenario_label/1, pass_fail_label/1, signature/1, deterministic/1, scenario/1, outcomes/1, failed_scenarios/1, nondeterministic_failures/1, report_signature/1, snapshot_signature/0, snapshot_report_markdown/0]).
-export_type([scenario/0, scenario_outcome/0, report/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(" Deterministic tenant isolation and policy runtime harness (M29).\n").
-type scenario() :: tenant_boundary_authz |
denial_telemetry_observable |
per_tenant_budget_quotas |
noisy_neighbor_containment |
operator_auditability.
-type scenario_outcome() :: {scenario_outcome,
scenario(),
boolean(),
boolean(),
binary()}.
-type report() :: {report, list(scenario_outcome()), integer(), integer()}.
-file("src/lightspeed/ops/tenant_harness.gleam", 378).
-spec count_nondeterministic(list(scenario_outcome())) -> integer().
count_nondeterministic(Outcomes) ->
case Outcomes of
[] ->
0;
[Outcome | Rest] ->
case erlang:element(4, Outcome) of
true ->
count_nondeterministic(Rest);
false ->
1 + count_nondeterministic(Rest)
end
end.
-file("src/lightspeed/ops/tenant_harness.gleam", 367).
-spec count_failed(list(scenario_outcome())) -> integer().
count_failed(Outcomes) ->
case Outcomes of
[] ->
0;
[Outcome | Rest] ->
case erlang:element(3, Outcome) of
true ->
count_failed(Rest);
false ->
1 + count_failed(Rest)
end
end.
-file("src/lightspeed/ops/tenant_harness.gleam", 393).
-spec join_with(binary(), list(binary())) -> binary().
join_with(Separator, Values) ->
case Values of
[] ->
<<""/utf8>>;
[Value] ->
Value;
[Value@1 | Rest] ->
<<<<Value@1/binary, Separator/binary>>/binary,
(join_with(Separator, Rest))/binary>>
end.
-file("src/lightspeed/ops/tenant_harness.gleam", 389).
-spec contains_substring(binary(), binary()) -> boolean().
contains_substring(Value, Part) ->
gleam_stdlib:contains_string(Value, Part).
-file("src/lightspeed/ops/tenant_harness.gleam", 346).
-spec evaluate_operator_auditability() -> {boolean(), binary()}.
evaluate_operator_auditability() ->
Runtime = lightspeed@tenant@policy:new(
lightspeed@tenant@policy:tenant_context(
<<"actor-d"/utf8>>,
<<"tenant-d"/utf8>>,
viewer
),
lightspeed@tenant@policy:budget(1, 1, 1)
),
{Runtime@1, _} = lightspeed@tenant@policy:evaluate(
Runtime,
{write, <<"tenant-d"/utf8>>}
),
{Runtime@2, _} = lightspeed@tenant@policy:evaluate(
Runtime@1,
{open_session, 2}
),
Denials = lightspeed@tenant@policy:denials(Runtime@2),
Labels = gleam@list:map(
Denials,
fun lightspeed@tenant@policy:denial_label/1
),
Passed = (((erlang:length(Denials) =:= 2) andalso contains_substring(
join_with(<<";"/utf8>>, Labels),
<<"role_forbidden"/utf8>>
))
andalso contains_substring(
join_with(<<";"/utf8>>, Labels),
<<"session_quota_exceeded"/utf8>>
))
andalso contains_substring(
join_with(<<";"/utf8>>, Labels),
<<"tenant=tenant-d"/utf8>>
),
{Passed, <<"audit="/utf8, (join_with(<<";"/utf8>>, Labels))/binary>>}.
-file("src/lightspeed/ops/tenant_harness.gleam", 305).
-spec evaluate_noisy_neighbor_containment() -> {boolean(), binary()}.
evaluate_noisy_neighbor_containment() ->
A_runtime = lightspeed@tenant@policy:new(
lightspeed@tenant@policy:tenant_context(
<<"actor-a"/utf8>>,
<<"tenant-a"/utf8>>,
tenant_admin
),
lightspeed@tenant@policy:budget(2, 2, 2)
),
B_runtime = lightspeed@tenant@policy:new(
lightspeed@tenant@policy:tenant_context(
<<"actor-b"/utf8>>,
<<"tenant-b"/utf8>>,
tenant_admin
),
lightspeed@tenant@policy:budget(2, 2, 2)
),
{A_runtime@1, _} = lightspeed@tenant@policy:evaluate(
A_runtime,
{emit_event, 1}
),
{A_runtime@2, _} = lightspeed@tenant@policy:evaluate(
A_runtime@1,
{emit_event, 1}
),
{A_runtime@3, A_overflow} = lightspeed@tenant@policy:evaluate(
A_runtime@2,
{emit_event, 1}
),
{B_runtime@1, B_event_1} = lightspeed@tenant@policy:evaluate(
B_runtime,
{emit_event, 1}
),
{B_runtime@2, B_event_2} = lightspeed@tenant@policy:evaluate(
B_runtime@1,
{emit_event, 1}
),
{B_runtime@3, B_session} = lightspeed@tenant@policy:evaluate(
B_runtime@2,
{open_session, 1}
),
Passed = (((((((lightspeed@tenant@policy:outcome_label(A_overflow) =:= <<"denied:event_quota_exceeded"/utf8>>)
andalso (lightspeed@tenant@policy:outcome_label(B_event_1) =:= <<"allowed:within_budget"/utf8>>))
andalso (lightspeed@tenant@policy:outcome_label(B_event_2) =:= <<"allowed:within_budget"/utf8>>))
andalso (lightspeed@tenant@policy:outcome_label(B_session) =:= <<"allowed:within_budget"/utf8>>))
andalso (lightspeed@tenant@policy:usage_label(
lightspeed@tenant@policy:runtime_usage(A_runtime@3)
)
=:= <<"events=2,sessions=0,jobs=0"/utf8>>))
andalso (lightspeed@tenant@policy:usage_label(
lightspeed@tenant@policy:runtime_usage(B_runtime@3)
)
=:= <<"events=2,sessions=1,jobs=0"/utf8>>))
andalso (erlang:length(lightspeed@tenant@policy:denials(A_runtime@3)) =:= 1))
andalso (lightspeed@tenant@policy:denials(B_runtime@3) =:= []),
{Passed,
<<<<<<"a="/utf8,
(lightspeed@tenant@policy:signature(A_runtime@3))/binary>>/binary,
"|b="/utf8>>/binary,
(lightspeed@tenant@policy:signature(B_runtime@3))/binary>>}.
-file("src/lightspeed/ops/tenant_harness.gleam", 268).
-spec evaluate_per_tenant_budget_quotas() -> {boolean(), binary()}.
evaluate_per_tenant_budget_quotas() ->
Runtime = lightspeed@tenant@policy:new(
lightspeed@tenant@policy:tenant_context(
<<"actor-c"/utf8>>,
<<"tenant-c"/utf8>>,
tenant_admin
),
lightspeed@tenant@policy:budget(2, 1, 1)
),
{Runtime@1, E1} = lightspeed@tenant@policy:evaluate(
Runtime,
{emit_event, 1}
),
{Runtime@2, E2} = lightspeed@tenant@policy:evaluate(
Runtime@1,
{emit_event, 1}
),
{Runtime@3, E3} = lightspeed@tenant@policy:evaluate(
Runtime@2,
{emit_event, 1}
),
{Runtime@4, S1} = lightspeed@tenant@policy:evaluate(
Runtime@3,
{open_session, 1}
),
{Runtime@5, S2} = lightspeed@tenant@policy:evaluate(
Runtime@4,
{open_session, 1}
),
{Runtime@6, J1} = lightspeed@tenant@policy:evaluate(
Runtime@5,
{start_job, 1}
),
{Runtime@7, J2} = lightspeed@tenant@policy:evaluate(
Runtime@6,
{start_job, 1}
),
Usage = lightspeed@tenant@policy:usage_label(
lightspeed@tenant@policy:runtime_usage(Runtime@7)
),
Denials = lightspeed@tenant@policy:denials(Runtime@7),
Passed = ((((((((lightspeed@tenant@policy:outcome_label(E1) =:= <<"allowed:within_budget"/utf8>>)
andalso (lightspeed@tenant@policy:outcome_label(E2) =:= <<"allowed:within_budget"/utf8>>))
andalso (lightspeed@tenant@policy:outcome_label(E3) =:= <<"denied:event_quota_exceeded"/utf8>>))
andalso (lightspeed@tenant@policy:outcome_label(S1) =:= <<"allowed:within_budget"/utf8>>))
andalso (lightspeed@tenant@policy:outcome_label(S2) =:= <<"denied:session_quota_exceeded"/utf8>>))
andalso (lightspeed@tenant@policy:outcome_label(J1) =:= <<"allowed:within_budget"/utf8>>))
andalso (lightspeed@tenant@policy:outcome_label(J2) =:= <<"denied:job_quota_exceeded"/utf8>>))
andalso (Usage =:= <<"events=2,sessions=1,jobs=1"/utf8>>))
andalso (erlang:length(Denials) =:= 3),
{Passed,
<<<<<<"usage="/utf8, Usage/binary>>/binary, "|denials="/utf8>>/binary,
(join_with(
<<","/utf8>>,
gleam@list:map(
Denials,
fun(Denial) -> erlang:element(5, Denial) end
)
))/binary>>}.
-file("src/lightspeed/ops/tenant_harness.gleam", 231).
-spec evaluate_denial_telemetry_observable() -> {boolean(), binary()}.
evaluate_denial_telemetry_observable() ->
Runtime = lightspeed@tenant@policy:new(
lightspeed@tenant@policy:tenant_context(
<<"actor-b"/utf8>>,
<<"tenant-b"/utf8>>,
editor
),
lightspeed@tenant@policy:budget(1, 1, 1)
),
{Runtime@1, Event_ok} = lightspeed@tenant@policy:evaluate(
Runtime,
{emit_event, 1}
),
{Runtime@2, Event_denied} = lightspeed@tenant@policy:evaluate(
Runtime@1,
{emit_event, 1}
),
Telemetry_line = case Event_denied of
{denied, Denial} ->
lightspeed@ops@telemetry:metric_line(
lightspeed@tenant@policy:denial_metric(Denial)
);
_ ->
<<"none"/utf8>>
end,
Passed = (((((lightspeed@tenant@policy:outcome_label(Event_ok) =:= <<"allowed:within_budget"/utf8>>)
andalso (lightspeed@tenant@policy:outcome_label(Event_denied) =:= <<"denied:event_quota_exceeded"/utf8>>))
andalso contains_substring(
Telemetry_line,
<<"lightspeed.tenant.policy_denied_total"/utf8>>
))
andalso contains_substring(Telemetry_line, <<"tenant_id=tenant-b"/utf8>>))
andalso contains_substring(
Telemetry_line,
<<"reason=event_quota_exceeded"/utf8>>
))
andalso (erlang:length(lightspeed@tenant@policy:denials(Runtime@2)) =:= 1),
{Passed,
<<<<<<<<<<"event_ok="/utf8,
(lightspeed@tenant@policy:outcome_label(Event_ok))/binary>>/binary,
"|event_denied="/utf8>>/binary,
(lightspeed@tenant@policy:outcome_label(Event_denied))/binary>>/binary,
"|telemetry="/utf8>>/binary,
Telemetry_line/binary>>}.
-file("src/lightspeed/ops/tenant_harness.gleam", 189).
-spec evaluate_tenant_boundary_authz() -> {boolean(), binary()}.
evaluate_tenant_boundary_authz() ->
Context = lightspeed@tenant@policy:tenant_context(
<<"actor-a"/utf8>>,
<<"tenant-a"/utf8>>,
viewer
),
Runtime = lightspeed@tenant@policy:new(
Context,
lightspeed@tenant@policy:default_budget()
),
Scope = lightspeed@tenant@policy:repository_scope(Context),
{Runtime@1, Read_own} = lightspeed@tenant@policy:evaluate(
Runtime,
{read, <<"tenant-a"/utf8>>}
),
{Runtime@2, Read_other} = lightspeed@tenant@policy:evaluate(
Runtime@1,
{read, <<"tenant-b"/utf8>>}
),
{Runtime@3, Write_own} = lightspeed@tenant@policy:evaluate(
Runtime@2,
{write, <<"tenant-a"/utf8>>}
),
{Runtime@4, Delete_other} = lightspeed@tenant@policy:evaluate(
Runtime@3,
{delete, <<"tenant-b"/utf8>>}
),
{System_runtime, System_delete} = begin
_pipe = lightspeed@tenant@policy:new(
lightspeed@tenant@policy:system_context(<<"svc-1"/utf8>>),
lightspeed@tenant@policy:default_budget()
),
lightspeed@tenant@policy:evaluate(_pipe, {delete, <<"tenant-z"/utf8>>})
end,
Passed = (((((lightspeed@tenant@policy:valid(Runtime@4) andalso (lightspeed@data@repository:scope_label(
Scope
)
=:= <<"tenant_scope:actor-a:tenant-a:viewer"/utf8>>))
andalso (lightspeed@tenant@policy:outcome_label(Read_own) =:= <<"allowed:tenant_match"/utf8>>))
andalso (lightspeed@tenant@policy:outcome_label(Read_other) =:= <<"denied:tenant_mismatch:tenant-b"/utf8>>))
andalso (lightspeed@tenant@policy:outcome_label(Write_own) =:= <<"denied:role_forbidden"/utf8>>))
andalso (lightspeed@tenant@policy:outcome_label(Delete_other) =:= <<"denied:tenant_mismatch:tenant-b"/utf8>>))
andalso (lightspeed@tenant@policy:outcome_label(System_delete) =:= <<"allowed:system_scope"/utf8>>),
{Passed,
<<<<<<<<<<<<<<<<<<<<<<"scope="/utf8,
(lightspeed@data@repository:scope_label(
Scope
))/binary>>/binary,
"|read_own="/utf8>>/binary,
(lightspeed@tenant@policy:outcome_label(
Read_own
))/binary>>/binary,
"|read_other="/utf8>>/binary,
(lightspeed@tenant@policy:outcome_label(
Read_other
))/binary>>/binary,
"|write_own="/utf8>>/binary,
(lightspeed@tenant@policy:outcome_label(Write_own))/binary>>/binary,
"|delete_other="/utf8>>/binary,
(lightspeed@tenant@policy:outcome_label(Delete_other))/binary>>/binary,
"|system="/utf8>>/binary,
(lightspeed@tenant@policy:signature(System_runtime))/binary>>}.
-file("src/lightspeed/ops/tenant_harness.gleam", 179).
-spec evaluate(scenario()) -> {boolean(), binary()}.
evaluate(Scenario) ->
case Scenario of
tenant_boundary_authz ->
evaluate_tenant_boundary_authz();
denial_telemetry_observable ->
evaluate_denial_telemetry_observable();
per_tenant_budget_quotas ->
evaluate_per_tenant_budget_quotas();
noisy_neighbor_containment ->
evaluate_noisy_neighbor_containment();
operator_auditability ->
evaluate_operator_auditability()
end.
-file("src/lightspeed/ops/tenant_harness.gleam", 60).
?DOC(" Run one scenario twice and require deterministic parity.\n").
-spec run_scenario(scenario()) -> scenario_outcome().
run_scenario(Scenario) ->
{First_passed, First_signature} = evaluate(Scenario),
{Second_passed, Second_signature} = evaluate(Scenario),
Deterministic = (First_passed =:= Second_passed) andalso (First_signature
=:= Second_signature),
Passed = (First_passed andalso Second_passed) andalso Deterministic,
{scenario_outcome, Scenario, Passed, Deterministic, First_signature}.
-file("src/lightspeed/ops/tenant_harness.gleam", 41).
?DOC(" Run all M29 scenarios.\n").
-spec run_matrix() -> report().
run_matrix() ->
Outcomes = begin
_pipe = [tenant_boundary_authz,
denial_telemetry_observable,
per_tenant_budget_quotas,
noisy_neighbor_containment,
operator_auditability],
gleam@list:map(_pipe, fun run_scenario/1)
end,
{report, Outcomes, count_failed(Outcomes), count_nondeterministic(Outcomes)}.
-file("src/lightspeed/ops/tenant_harness.gleam", 76).
?DOC(" Scenario label.\n").
-spec scenario_label(scenario()) -> binary().
scenario_label(Scenario) ->
case Scenario of
tenant_boundary_authz ->
<<"tenant_boundary_authz"/utf8>>;
denial_telemetry_observable ->
<<"denial_telemetry_observable"/utf8>>;
per_tenant_budget_quotas ->
<<"per_tenant_budget_quotas"/utf8>>;
noisy_neighbor_containment ->
<<"noisy_neighbor_containment"/utf8>>;
operator_auditability ->
<<"operator_auditability"/utf8>>
end.
-file("src/lightspeed/ops/tenant_harness.gleam", 87).
?DOC(" Stable pass/fail label.\n").
-spec pass_fail_label(scenario_outcome()) -> binary().
pass_fail_label(Outcome) ->
case erlang:element(3, Outcome) of
true ->
<<"pass"/utf8>>;
false ->
<<"fail"/utf8>>
end.
-file("src/lightspeed/ops/tenant_harness.gleam", 95).
?DOC(" Scenario signature.\n").
-spec signature(scenario_outcome()) -> binary().
signature(Outcome) ->
erlang:element(5, Outcome).
-file("src/lightspeed/ops/tenant_harness.gleam", 100).
?DOC(" Determinism accessor.\n").
-spec deterministic(scenario_outcome()) -> boolean().
deterministic(Outcome) ->
erlang:element(4, Outcome).
-file("src/lightspeed/ops/tenant_harness.gleam", 105).
?DOC(" Scenario accessor.\n").
-spec scenario(scenario_outcome()) -> scenario().
scenario(Outcome) ->
erlang:element(2, Outcome).
-file("src/lightspeed/ops/tenant_harness.gleam", 110).
?DOC(" Report outcomes.\n").
-spec outcomes(report()) -> list(scenario_outcome()).
outcomes(Report) ->
erlang:element(2, Report).
-file("src/lightspeed/ops/tenant_harness.gleam", 115).
?DOC(" Failed scenario count.\n").
-spec failed_scenarios(report()) -> integer().
failed_scenarios(Report) ->
erlang:element(3, Report).
-file("src/lightspeed/ops/tenant_harness.gleam", 120).
?DOC(" Nondeterministic failure count.\n").
-spec nondeterministic_failures(report()) -> integer().
nondeterministic_failures(Report) ->
erlang:element(4, Report).
-file("src/lightspeed/ops/tenant_harness.gleam", 401).
-spec bool_label(boolean()) -> binary().
bool_label(Value) ->
case Value of
true ->
<<"true"/utf8>>;
false ->
<<"false"/utf8>>
end.
-file("src/lightspeed/ops/tenant_harness.gleam", 125).
?DOC(" Stable report signature.\n").
-spec report_signature(report()) -> binary().
report_signature(Report) ->
Entries = gleam@list:map(
erlang:element(2, Report),
fun(Outcome) ->
<<<<<<<<<<<<(scenario_label(erlang:element(2, Outcome)))/binary,
"="/utf8>>/binary,
(pass_fail_label(Outcome))/binary>>/binary,
":deterministic="/utf8>>/binary,
(bool_label(erlang:element(4, Outcome)))/binary>>/binary,
":"/utf8>>/binary,
(erlang:element(5, Outcome))/binary>>
end
),
join_with(<<";"/utf8>>, Entries).
-file("src/lightspeed/ops/tenant_harness.gleam", 141).
?DOC(" Deterministic snapshot signature for fixture drift gates.\n").
-spec snapshot_signature() -> binary().
snapshot_signature() ->
<<<<<<"m29.snapshot.v"/utf8, (erlang:integer_to_binary(1))/binary>>/binary,
"|"/utf8>>/binary,
(report_signature(run_matrix()))/binary>>.
-file("src/lightspeed/ops/tenant_harness.gleam", 149).
?DOC(" Deterministic markdown report for fixture scripts.\n").
-spec snapshot_report_markdown() -> binary().
snapshot_report_markdown() ->
Report = run_matrix(),
Failed = failed_scenarios(Report),
Nondeterministic = nondeterministic_failures(Report),
Status = case (Failed =:= 0) andalso (Nondeterministic =:= 0) of
true ->
<<"OK"/utf8>>;
false ->
<<"FAIL"/utf8>>
end,
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"# Tenant Fixture Report\n\n"/utf8,
"snapshot_version: "/utf8>>/binary,
(erlang:integer_to_binary(
1
))/binary>>/binary,
"\n"/utf8>>/binary,
"status: "/utf8>>/binary,
Status/binary>>/binary,
"\n"/utf8>>/binary,
"failed_scenarios: "/utf8>>/binary,
(erlang:integer_to_binary(
Failed
))/binary>>/binary,
"\n"/utf8>>/binary,
"nondeterministic_failures: "/utf8>>/binary,
(erlang:integer_to_binary(Nondeterministic))/binary>>/binary,
"\n\n"/utf8>>/binary,
"snapshot_signature: "/utf8>>/binary,
(snapshot_signature())/binary>>/binary,
"\n\n"/utf8>>/binary,
"report_signature: "/utf8>>/binary,
(report_signature(Report))/binary>>/binary,
"\n"/utf8>>.