Skip to main content

sms-firewall-service — Local Dev Setup

Version: 1.0 Status: Draft Owner: Trust and Safety + DevEx Last Updated: 2026-04-21 References: SERVICE_OVERVIEW.md, DEPLOYMENT_TOPOLOGY.md

Local development recipe for sms-firewall-service. Uses Docker Compose to stand up the service plus all infrastructure dependencies and mocks for external systems.


1. Prerequisites

ToolVersionNotes
Node.js20 LTSUse nvm
pnpm9.xcorepack enable
Docker Engine24+With Compose v2
grpcurlLatestFor gRPC testing
psql16Postgres client
redis-cli7Redis client
nats CLI0.1.5+NATS testing
jqlatestJSON processing

2. Repository Layout

Ghasi-SMS-Gateway/
services/sms-firewall-service/ ← this service spec
(application repo: Ghasi-edTech equivalent)/services/sms-firewall-service/ ← code
infra/docker/docker-compose.sms-firewall.yml

3. Docker Compose Recipe

Create infra/docker/docker-compose.sms-firewall.yml:

version: '3.9'
services:
postgres-firewall:
image: postgres:16-alpine
environment:
POSTGRES_DB: firewall
POSTGRES_USER: firewall
POSTGRES_PASSWORD: firewall
ports: ["5444:5432"]
volumes:
- firewall_pg_data:/var/lib/postgresql/data
- ./firewall-schema.sql:/docker-entrypoint-initdb.d/001-schema.sql:ro
- ./firewall-seed.sql:/docker-entrypoint-initdb.d/002-seed.sql:ro

redis-firewall:
image: redis:7-alpine
ports: ["6394:6379"]
command: redis-server --appendonly yes

nats:
image: nats:2.10-alpine
command: -js -m 8222
ports: ["4222:4222", "8222:8222"]

mock-fraud-feed:
image: ghasi/mock-fraud-feed:dev
ports: ["7100:7100"]
environment:
FEED_EMIT_INTERVAL_MS: 5000
FEED_AIT_PROB: 0.1
FEED_SIMBOX_PROB: 0.05

mock-mno-blocklist:
image: ghasi/mock-mno-blocklist:dev
ports: ["7200:7200"]
volumes:
- ./sample-national-blocklist.csv:/data/blocklist.csv:ro

mock-consent-ledger:
image: ghasi/mock-grpc-consent:dev
ports: ["50071:50071"]

mock-sender-id-registry:
image: ghasi/mock-grpc-senderid:dev
ports: ["50091:50091"]

sms-firewall:
image: ghasi/sms-firewall-service:dev
depends_on: [postgres-firewall, redis-firewall, nats, mock-fraud-feed, mock-mno-blocklist, mock-consent-ledger, mock-sender-id-registry]
ports: ["3051:3051", "50051:50051"]
environment:
NODE_ENV: development
LOG_LEVEL: debug
DATABASE_URL: postgres://firewall:firewall@postgres-firewall:5432/firewall
REDIS_URL: redis://redis-firewall:6379/0
NATS_URL: nats://nats:4222
GRPC_PORT: 50051
HTTP_PORT: 3051
GRPC_TLS_ENABLED: "false"
FRAUD_FEED_URL: http://mock-fraud-feed:7100
MNO_BLOCKLIST_URL: http://mock-mno-blocklist:7200
CONSENT_LEDGER_GRPC: mock-consent-ledger:50071
SENDER_ID_REGISTRY_GRPC: mock-sender-id-registry:50091
# Feature flags (local defaults)
ML_MODEL_ENABLED: "false" # Rule-based only in dev
FEDERATION_ENABLED: "true"
FIREWALL_EMERGENCY_BYPASS: "false"

volumes:
firewall_pg_data:

Start:

docker compose -f infra/docker/docker-compose.sms-firewall.yml up -d

Verify health:

curl -s http://localhost:3051/health/ready | jq
grpcurl -plaintext localhost:50051 list

4. Seed Data

firewall-seed.sql (in infra/docker/):

-- Sample firewall rules
INSERT INTO firewall.rules (id, name, direction, action, predicate_json, severity, enabled) VALUES
('rule_ait_base', 'AIT high-confidence', 'TRANSIT', 'BLOCK', '{"type":"ml","model":"ait-v1","threshold":0.92}', 'HIGH', true),
('rule_simbox_base', 'SIM-box pattern', 'INBOUND', 'QUARANTINE', '{"type":"ml","model":"simbox-v1","threshold":0.85}', 'HIGH', true),
('rule_consent_block', 'No consent → block', 'TRANSIT', 'BLOCK', '{"type":"consent","require":"granted"}', 'CRITICAL', true),
('rule_senderid_ver', 'Unverified sender on P0/P1', 'TRANSIT', 'BLOCK', '{"type":"senderid","require":"NOTARISED"}', 'HIGH', true),
('rule_geo_af_only', 'Afghanistan-only inbound', 'INBOUND', 'BLOCK', '{"type":"geo","allow":["AF"]}', 'MEDIUM', true);

-- Sample blocklist entries
INSERT INTO firewall.blocklists (id, name, type) VALUES
('bl_national_seed', 'National seed blocklist', 'MSISDN');

INSERT INTO firewall.blocklist_entries (blocklist_id, pattern, pattern_type, ttl_seconds, reason) VALUES
('bl_national_seed', '+93700000000', 'EXACT', NULL, 'Test reserved MSISDN'),
('bl_national_seed', '+93701111111', 'EXACT', NULL, 'Sample blocked subscriber');

-- SIM-box known patterns (for rule-based fallback)
INSERT INTO firewall.simbox_signals (key, pattern_json) VALUES
('simbox_high_volume_in', '{"inbound_per_msisdn_5min": {"gte": 500}}'),
('simbox_origin_cluster', '{"distinct_originator_cidr_ratio": {"lt": 0.1}}');

5. Common Commands

5.1 Verify gRPC shape

grpcurl -plaintext localhost:50051 list ghasi.firewall.v1.SmsFirewallService

5.2 Test EvaluateTransit (MT outbound filter)

grpcurl -plaintext -d '{
"messageId": "msg_test_001",
"tenantId": "tnt_demo",
"senderId": "TESTBRAND",
"recipientMsisdn": "+93701234567",
"lane": "P3",
"contentFingerprint": "abc123..."
}' localhost:50051 ghasi.firewall.v1.SmsFirewallService/EvaluateTransit

Expected:

{
"verdict": "ALLOW",
"reason": "",
"cachedAt": "2026-04-21T08:00:00Z",
"evidence": []
}

5.3 Test FilterInbound (MO inbound filter)

grpcurl -plaintext -d '{
"originatorMsisdn": "+93701234567",
"destinationShortcode": "12345",
"content": "Hello from subscriber",
"receivedAt": "2026-04-21T08:00:00Z"
}' localhost:50051 ghasi.firewall.v1.SmsFirewallService/FilterInbound

5.4 Trigger a blocklist-match test

grpcurl -plaintext -d '{
"messageId":"msg_blocked_001",
"tenantId":"tnt_demo",
"senderId":"TESTBRAND",
"recipientMsisdn":"+93700000000",
"lane":"P3",
"contentFingerprint":"x"
}' localhost:50051 ghasi.firewall.v1.SmsFirewallService/EvaluateTransit

Expected: verdict: BLOCK, reason: "BLOCKLIST_MATCH".

5.5 Simulate a fraud signal arrival

nats pub fraud.detected.ait.v1 '{"msisdn":"+93701234567","confidence":0.95,"detectedAt":"2026-04-21T08:00:00Z","campaignId":"camp_001"}'

Re-run EvaluateTransit for the same MSISDN → expect verdict to flip to BLOCK.

5.6 Inspect the audit chain

psql postgres://firewall:firewall@localhost:5444/firewall -c "
SELECT id, decision_at, action, rule_id, encode(record_hash, 'hex') as record_hash
FROM firewall.audit
ORDER BY decision_at DESC
LIMIT 10;
"

5.7 Verify chain integrity locally

pnpm --filter @ghasi/sms-firewall-service audit:verify -- --from 2026-04-21 --to 2026-04-22

5.8 Run tests

# Unit
pnpm --filter @ghasi/sms-firewall-service test

# Integration (requires docker-compose up)
pnpm --filter @ghasi/sms-firewall-service test:integration

# Contract
pnpm --filter @ghasi/sms-firewall-service test:contract

# Load (lightweight local benchmark)
pnpm --filter @ghasi/sms-firewall-service test:load:local

5.9 Reset state (for deterministic tests)

docker compose -f infra/docker/docker-compose.sms-firewall.yml down -v
docker compose -f infra/docker/docker-compose.sms-firewall.yml up -d

6. Environment Variables Reference

VariableDefault (dev)Purpose
NODE_ENVdevelopmentStandard Node env
LOG_LEVELdebugPino log level
DATABASE_URLpostgres://...Firewall schema
REDIS_URLredis://...Hot-cache
NATS_URLnats://nats:4222Event bus
GRPC_PORT50051gRPC listener
HTTP_PORT3051HTTP admin / health
GRPC_TLS_ENABLEDfalsemTLS off in dev
FRAUD_FEED_URLmockFraud-intel signal source
MNO_BLOCKLIST_URLmockNational-blocklist federation source
CONSENT_LEDGER_GRPCmockConsent gRPC target
SENDER_ID_REGISTRY_GRPCmockSender-ID gRPC target
ML_MODEL_ENABLEDfalseDisable ML in dev; rule-based only
FEDERATION_ENABLEDtrueFederation sync active
FIREWALL_EMERGENCY_BYPASSfalseEmergency-bypass flag (prod only)

7. Troubleshooting

SymptomLikely causeFix
/health/ready returns 503Postgres / Redis / NATS not updocker compose ps, restart deps
gRPC returns UNAVAILABLEServer not listening on :50051Check service logs; verify GRPC_PORT
All verdicts say ALLOWRules not seeded or disabledRe-run firewall-seed.sql; check firewall.rules.enabled
Fraud signals not arrivingmock-fraud-feed not running or NATS subject mismatchdocker compose logs mock-fraud-feed; nats stream ls
Audit chain verification failsSeed file bug or manual UPDATE on firewall.auditReset volume (§5.9) and re-seed
High latency in devRedis + Postgres container warm-upGive 30 s after up -d before load

8. Running with Kubernetes (kind) Instead of Compose

For those preferring kind:

kind create cluster --name firewall-dev
helm dependency build infra/helm/sms-firewall-service
helm install firewall infra/helm/sms-firewall-service -f infra/helm/sms-firewall-service/values.dev.yaml

See infra/helm/sms-firewall-service/ (forthcoming) for full chart.