Class: Insika::Commands::DeleteTenantData

Inherits:
Object
  • Object
show all
Includes:
SessionPurge
Defined in:
lib/insika/commands/delete_tenant_data.rb

Overview

Control command (WS8, phase 2 — LGPD): purges everything the engine holds about ONE TENANT — its sessions (the ":" namespace) and everything those sessions left behind (traces, tasks, checkpoints, outbox deliveries — see SessionPurge), every memory cell under the tenant (its own + the customer cells), its outcome records (WS7) and its API CREDENTIALS (every active token of the tenant is revoked first — an offboarded tenant must not authenticate). The tenant string IS the isolation boundary, so zeroing it cannot touch another tenant's data. Operator-only BY CONSTRUCTION: the generic command ingress is operator-grade (a tenant principal never reaches it).

Instance Method Summary collapse

Methods included from SessionPurge

#purge_sessions

Constructor Details

#initialize(memory_store:, session_store:, tool_trace_store: nil, context_trace_store: nil, model_visible_trace_store: nil, outcome_store: nil, task_store: nil, checkpoint_store: nil, outbox_store: nil, shadow_pairs: nil, token_store: nil, funnel_store: nil, event_stream:, followup_store: nil, contact_store: nil, proposal_store: nil, harvest_store: nil, schedule_store: nil, artifact_store: nil) ⇒ DeleteTenantData

Returns a new instance of DeleteTenantData.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/insika/commands/delete_tenant_data.rb', line 20

def initialize(memory_store:, session_store:, tool_trace_store: nil,
               context_trace_store: nil, model_visible_trace_store: nil,
               outcome_store: nil, task_store: nil,
               checkpoint_store: nil, outbox_store: nil, shadow_pairs: nil,
                token_store: nil, funnel_store: nil, event_stream:,
               followup_store: nil, contact_store: nil, proposal_store: nil,
               harvest_store: nil, schedule_store: nil, artifact_store: nil)
  @memory_store = memory_store
  @token_store = token_store
  @session_store = session_store
  @tool_trace_store = tool_trace_store
  @context_trace_store = context_trace_store
  @model_visible_trace_store = model_visible_trace_store #  ; nil = parity
  @outcome_store = outcome_store
  @task_store = task_store
  @checkpoint_store = checkpoint_store
  @outbox_store = outbox_store
  @shadow_pairs = shadow_pairs
  @funnel_store = funnel_store #  ; nil = nothing to sweep
  @followup_store = followup_store #  ; nil = nothing to sweep
  @contact_store = contact_store   #  ; nil = nothing to sweep
  @proposal_store = proposal_store #  ; nil = nothing to sweep
  @harvest_store = harvest_store   #  ; nil = nothing to sweep
  @schedule_store = schedule_store #  ; nil = nothing to sweep
  @artifact_store = artifact_store #  ; nil = nothing to sweep
  @event_stream = event_stream
end

Instance Method Details

#call(command) ⇒ Object

-> { tenant:, sessions:, memory_records:, outcomes:, tokens_revoked:, tasks:, checkpoints:, deliveries:, followups:, contacts: }.

Raises:



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/insika/commands/delete_tenant_data.rb', line 50

def call(command)
  tenant = Coercion.presence(command.payload[:tenant] || command.payload["tenant"])
  raise ValidationError, "tenant is required" if tenant.nil?

  # CREDENTIALS FIRST (WS1+WS8): erasing the data while the tenant's
  # tokens still resolve leaves an offboarded tenant authenticating and
  # opening a NEW session — the purge would report success over a live
  # customer. Revoking before the sweep closes the door, so nothing the
  # tenant does mid-purge survives it. nil store = single_tenant mode
  # (no per-tenant credential exists).
  tokens_revoked = @token_store ? @token_store.revoke_all(tenant_id: tenant) : 0

  sessions = @session_store.each_id.select { |id| id.to_s.start_with?("#{tenant}:") }
  purged = purge_sessions(sessions)

  memory_records = @memory_store.purge_tenant(tenant)
  outcomes = @outcome_store ? @outcome_store.purge(tenant: tenant) : 0
  funnel = @funnel_store ? @funnel_store.purge(tenant: tenant) : 0
  # the follow-up footprint dies with the tenant — records
  # and contact cells under the same tenant prefix.
  followups = @followup_store ? @followup_store.purge(tenant: tenant) : 0
  contacts = @contact_store ? @contact_store.purge(tenant: tenant) : 0
  # the distilled proposals die with the tenant.
  proposals = @proposal_store ? @proposal_store.purge(tenant: tenant) : 0
  # candidates reference sessions, and sessions carry the
  # tenant prefix — the harvest rows die with the tenant (D11).
  harvest = @harvest_store ? @harvest_store.purge(tenant: tenant) : 0
  # recurring-schedule rows die with the tenant too (their
  # message text is content, never kept behind an offboarded tenant).
  schedules = @schedule_store ? @schedule_store.purge(tenant: tenant) : 0
  # artifacts die with the tenant — a report is content, and the
  # tenant binding is the isolation boundary (never a model-typed id).
  artifacts = @artifact_store ? @artifact_store.purge(tenant: tenant) : 0

  @event_stream.emit(Insika::Event.new(
                       type: :tenant_data_deleted,
                       data: { tenant: tenant, sessions: sessions,
                               memory_records: memory_records,
                               outcomes: outcomes,
                               funnel: funnel,
                               followups: followups,
                               contacts: contacts,
                               proposals: proposals,
                               harvest: harvest,
                               schedules: schedules,
                               artifacts: artifacts,
                               tokens_revoked: tokens_revoked }.merge(purged),
                       meta: { at: Time.now.utc.iso8601 }
                     ))
  { tenant: tenant, sessions: sessions, memory_records: memory_records,
    outcomes: outcomes, funnel: funnel, followups: followups, contacts: contacts,
    proposals: proposals, harvest: harvest, schedules: schedules,
    artifacts: artifacts,
    tokens_revoked: tokens_revoked }.merge(purged)
end