feat(p9): route OutboundService through the Capability Broker
Task 9.3: OutboundService now delegates the execute step to CapabilityBroker (policy-gated + obligation-enforced) instead of calling a provider directly, while keeping idempotency + rate-limit + the command/attempt ledger. BLOCKED obligations → command BLOCKED; a fail-closed throw marks the command FAILED then propagates. AdaptersModule imports CapabilityModule; P4/P6/P8 egress now flows through the broker unchanged. Updated 6 spec constructions; added a DENY_OUTBOUND test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,8 @@ import { signedFixture, emailFixture } from '@insignia/iios-adapter-sdk';
|
||||
import { AdapterRegistry } from './adapter.registry';
|
||||
import { InboundService } from './inbound.service';
|
||||
import { OutboundService } from './outbound.service';
|
||||
import { CapabilityBroker } from '../capability/capability.broker';
|
||||
import { CapabilityProviderRegistry } from '../capability/capability.registry';
|
||||
import { RawEventProjector } from './raw-event.projector';
|
||||
import { IngestService } from '../interactions/ingest.service';
|
||||
import { ActorResolver } from '../identity/actor.resolver';
|
||||
@@ -77,7 +79,7 @@ describe('Adapter inbound (P5)', () => {
|
||||
|
||||
describe('Adapter outbound (P5)', () => {
|
||||
const outbound = (limit?: number): OutboundService => {
|
||||
const s = new OutboundService(asService, registry());
|
||||
const s = new OutboundService(asService, new CapabilityBroker(makeFakePorts(), new CapabilityProviderRegistry()));
|
||||
if (limit) s.limit = limit;
|
||||
return s;
|
||||
};
|
||||
@@ -104,4 +106,13 @@ describe('Adapter outbound (P5)', () => {
|
||||
expect(b.id).toBe(a.id);
|
||||
expect(await prisma.iiosOutboundCommand.count()).toBe(1);
|
||||
});
|
||||
|
||||
it('broker obligation DENY_OUTBOUND → command BLOCKED, no send (P9)', async () => {
|
||||
const ports = makeFakePorts();
|
||||
ports.opa.setDecision({ allow: true, obligations: [{ kind: 'DENY_OUTBOUND', reason: 'recipient opted out' }] });
|
||||
const svc = new OutboundService(asService, new CapabilityBroker(ports, new CapabilityProviderRegistry()));
|
||||
const cmd = await svc.send('WEBHOOK', 'user@x', { text: 'hi' }, 'blk-1');
|
||||
expect(cmd.status).toBe('BLOCKED');
|
||||
expect(cmd.providerRef).toBe('blocked');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user