From 5e6260864ef4c72e3f03c1ae0acc044c8e3ebefc Mon Sep 17 00:00:00 2001 From: archipelago Date: Fri, 9 Oct 2026 06:56:17 -0400 Subject: [PATCH] fix(indeehub): allow verified enabled UAT shutdown --- scripts/indeehub-maintenance-controller.py | 19 +++++++++++++++---- .../test_indeehub_maintenance_controller.py | 17 ++++++++++++++--- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/scripts/indeehub-maintenance-controller.py b/scripts/indeehub-maintenance-controller.py index e5871897..f96e704a 100644 --- a/scripts/indeehub-maintenance-controller.py +++ b/scripts/indeehub-maintenance-controller.py @@ -28,13 +28,24 @@ console.log(JSON.stringify({main_sha256:crypto.createHash('sha256').update(fs.re http_connections_absent:sockets.every(s=>{const c=s.trim().split(/\s+/);return !c[1].endsWith(':'+port)||['0A','06','07'].includes(c[3]);}), providers_absent:keys.every(k=>!process.env[k]), registration_disabled:process.env.ARCHIPELAGO_REGISTRATION_ENABLED==='false', - publication_disabled:process.env.ARCHIPELAGO_PUBLICATION_ENABLED==='false'}));''' + publication_disabled:process.env.ARCHIPELAGO_PUBLICATION_ENABLED==='false', + registration_enabled:process.env.ARCHIPELAGO_REGISTRATION_ENABLED==='true', + publication_enabled:process.env.ARCHIPELAGO_PUBLICATION_ENABLED==='true'}));''' def valid_money_free_uat(counts, probe): return (isinstance(counts,dict) and set(counts)==UAT_ZERO_COUNTS and all(type(v) is int and v==0 for v in counts.values()) - and isinstance(probe,dict) and all(type(probe.get(k)) is bool for k in ('providers_absent','http_connections_absent','registration_disabled','publication_disabled')) - and probe=={'main_sha256':UAT_API_MAIN_SHA256,'providers_absent':True,'http_connections_absent':True, - 'registration_disabled':True,'publication_disabled':True}) + and isinstance(probe,dict) and set(probe)=={'main_sha256','providers_absent','http_connections_absent', + 'registration_disabled','publication_disabled','registration_enabled','publication_enabled'} + and all(type(probe[k]) is bool for k in set(probe)-{'main_sha256'}) + and probe['main_sha256']==UAT_API_MAIN_SHA256 and probe['providers_absent'] is True + and probe['http_connections_absent'] is True + # Each feature must have an exact true/false value, and the pair must + # move together. Enabled private-UAT installs are safe only because the + # zero-count proof above also covers registration intents, publications, + # the publication outbox, entitlements, every payment table and revenue. + and probe['registration_disabled'] is not probe['registration_enabled'] + and probe['publication_disabled'] is not probe['publication_enabled'] + and probe['registration_enabled'] is probe['publication_enabled']) def valid_empty_business(counts): return isinstance(counts,dict) and set(counts)==BUSINESS_COUNTS and all(type(v) is int and v==0 for v in counts.values()) def valid_empty_queue(observed): diff --git a/tests/regression/test_indeehub_maintenance_controller.py b/tests/regression/test_indeehub_maintenance_controller.py index 56f19fa1..5b0928e6 100644 --- a/tests/regression/test_indeehub_maintenance_controller.py +++ b/tests/regression/test_indeehub_maintenance_controller.py @@ -446,21 +446,32 @@ console.log('process identity cases passed');''' self.assertEqual(c.record['legacy_api_empty_state'],counts) def test_money_free_uat_requires_all_monetary_history_and_capability_absent(self): counts=dict.fromkeys(module.UAT_ZERO_COUNTS,0) - probe={'main_sha256':module.UAT_API_MAIN_SHA256,'providers_absent':True,'http_connections_absent':True,'registration_disabled':True,'publication_disabled':True} + probe={'main_sha256':module.UAT_API_MAIN_SHA256,'providers_absent':True,'http_connections_absent':True,'registration_disabled':True,'publication_disabled':True,'registration_enabled':False,'publication_enabled':False} self.assertTrue(module.valid_money_free_uat(counts,probe)) + enabled=dict(probe,registration_disabled=False,publication_disabled=False,registration_enabled=True,publication_enabled=True) + self.assertTrue(module.valid_money_free_uat(counts,enabled)) for name in counts: for value in (1,-1,False,None): self.assertFalse(module.valid_money_free_uat(dict(counts,**{name:value}),probe)) missing=dict(counts);missing.pop(name) self.assertFalse(module.valid_money_free_uat(missing,probe)) for name in probe: - changed=dict(probe);changed[name]=False if name!='main_sha256' else '0'*64 + changed=dict(probe);changed[name]=not probe[name] if name!='main_sha256' else '0'*64 + self.assertFalse(module.valid_money_free_uat(counts,changed)) + for changed in ( + dict(probe,registration_disabled=False), + dict(probe,publication_disabled=False), + dict(probe,registration_enabled=True), + dict(probe,publication_enabled=True), + dict(enabled,registration_enabled=False), + dict(enabled,publication_enabled=False), + ): self.assertFalse(module.valid_money_free_uat(counts,changed)) def test_money_free_stopped_proof_rejects_changed_data_and_operation(self): import copy c=self.controller;m=next(m for m in members() if m['name']=='indeedhub-api') counts=dict.fromkeys(module.UAT_ZERO_COUNTS,0) - probe={'main_sha256':module.UAT_API_MAIN_SHA256,'providers_absent':True,'http_connections_absent':True,'registration_disabled':True,'publication_disabled':True} + probe={'main_sha256':module.UAT_API_MAIN_SHA256,'providers_absent':True,'http_connections_absent':True,'registration_disabled':True,'publication_disabled':True,'registration_enabled':False,'publication_enabled':False} baseline={'operation_id':self.operation,'tables':{'projects':{'rows':1,'rows_sha256':'a'*64}}} c.record={'money_free_uat':{'operation_id':self.operation,'container_id':m['container_id'],'image_id':m['image_id'],'counts':counts,'probe':probe,'database_before_signal':baseline}} c.holds=lambda:None;c.fence_matches=lambda:None;c.require_api_stopped=lambda _:None