Saudi Vision 2030 · National Transport & Logistics Strategy

Port Community
System

Enterprise digital transformation for King Abdulaziz Port, Dammam. Single API connecting shipping lines, freight forwarders, ZATCA customs, and port authority in real time.

Python 3.12 FastAPI React 18 PostgreSQL 16 Apache Kafka Redis 7 Kubernetes EKS Terraform AWS me-south-1
System Architecture Operational
🌐 ALB HTTPS · WAF
🐍 API Pod FastAPI
🐍 API Pod FastAPI
🐍 API Pod FastAPI
🐘 RDS PG Multi-AZ
Redis Cache
📡 Kafka 6 Topics
99.9% Uptime SLA
<50ms P50 Latency
30 REST APIs
0
TEUs / year handled
0
REST endpoints
0
Prometheus alerts
0
Full-stack build
Why PCS Exists

One API for the Entire Port Ecosystem

Before PCS, a single vessel arrival triggered 12+ manual data exchanges between stakeholders. Average cargo dwell time: 5.2 days. Saudi Vision 2030 targets 24 hours.

The Problem

Before PCS: Shipping lines faxed manifests. Customs officers re-typed data. Freight forwarders called the terminal by phone to check container status. Each stakeholder maintained separate bilateral data feeds — 12+ integrations per shipping line.
Average dwell time (before) 5.2 days
Target dwell time (Vision 2030) 24 hours

The Solution

With PCS: One authenticated API. Real-time vessel arrivals via AIS. Automatic customs declarations via ZATCA Fasah. Container status from discharge to gate-out. All stakeholders see the same data, simultaneously.
ZATCA Fasah Single Window — 100% electronic customs declarations
AIS feed updated every 15 minutes — real vessel ETAs
ISO 6346 container tracking from discharge to delivery
MAWANI-compliant 72-hour pre-arrival notifications
Saudi PDPL compliant — data residency in me-south-1 (Bahrain)
System Design

Architecture & Data Flows

Event-driven microservices on AWS EKS with Kafka as the backbone, RDS PostgreSQL for persistence, and ElastiCache Redis for session and rate management.

System Layers
🚢 Shipping Lines
·
📦 Freight Forwarders
·
🛃 Customs Officers
·
Port Authority
·
🚛 Trucking Cos.
HTTPS · JWT · API Key
🌐 AWS ALB SSL · WAF · HTTP→HTTPS
☸️ EKS — pcs-api (FastAPI) HPA: 3–10 pods · Rolling update maxUnavailable=0
🐘 RDS PostgreSQL 16 Multi-AZ · pcs_core · pcs_audit
·
ElastiCache Redis Sessions · Cache · Rate limits
·
📡 Amazon MSK Kafka 6 topics · SASL/SCRAM · KMS
📶 AIS Feed 15 min · MarineTraffic
·
🛂 ZATCA Fasah OAuth2 · Webhooks · SADAN
·
📊 Prometheus · Grafana 12 alerts · 10 metrics
🚢

Vessel Pre-Arrival Flow

Agent submits ETA → PCS validates 72h lead time → Kafka vessel.arrivals event → Berth planner notified → AIS feed begins tracking → VTS records ATA → Status: AT_ANCHORAGE

🛃

Customs Clearance Flow

Manifest ACCEPTED → POST /customs/submit → ZATCA OAuth2 token (Redis-cached) → Declaration submitted → Webhook CLEARED → Containers released → Kafka container.releases

📦

Container Release Flow

CUSTOMS_CLEARED → Duty paid → AVAILABLE_FOR_PICKUP → SMS notification via Unifonic → Truck gate-in → GATE_OUTDELIVERED confirmed

Platform Capabilities

Everything Port Operations Needs

Ten integrated modules covering the complete port community workflow from vessel pre-arrival to container delivery.

Vessel Call Management

Full lifecycle tracking: pre-arrival notification (72h MAWANI requirement), AIS position updates, berth assignment, status transitions with audit trail. Enforced state machine: EXPECTED → AT_ANCHORAGE → BERTHED → DEPARTED.

IMO · AIS · MAWANI

Cargo Manifest Submission

EDIFACT CUSCAR to JSON bridge. ISO 6346 container validation. Dangerous goods (IMDG class + UN number), reefer (temperature setpoint), out-of-gauge cargo. HS code 6–10 digit validation per Saudi ZATCA requirements.

EDIFACT · ISO 6346 · HS Codes

ZATCA Customs Integration

Direct OAuth2 integration with ZATCA Fasah Single Window. Automatic declaration submission, HMAC-verified webhook callbacks, Redis-cached access tokens, idempotent submission guard. Saudi Customs law compliant.

Fasah · OAuth2 · HMAC-SHA256

AIS Vessel Tracking

MarineTraffic / Spire AIS feed polling every 15 minutes. Vessel position, heading, speed, and ETA updates. Deviation alerting when AIS feed is stale for more than 20 minutes (VesselETAUpdateLag Prometheus alert).

AIS · IMO · MarineTraffic

Container Lifecycle Tracking

End-to-end status from MANIFESTED through DISCHARGED, YARD_IN, CUSTOMS_CLEARED, AVAILABLE_FOR_PICKUP, GATE_OUT to DELIVERED. Real-time webhook notifications. ISO 6346 check digit validated at middleware layer.

ISO 6346 · Webhooks · Redis Cache

Zero-Trust Security

JWT (60min) + API key auth. slowapi rate limiting (5/min login, 20/min customs). SQL injection middleware. Immutable audit log (PostgreSQL trigger + INSERT-only role). HSTS, CSP, X-Frame-Options on every response.

OWASP · PDPL · Audit

Prometheus Monitoring

10 custom port-domain metrics: vessel calls, customs backlog, AIS freshness, DB pool utilisation. 12 alert rules with port-operations context. 11-panel Grafana dashboard. CloudWatch Logs Insights compatible JSON logging.

Prometheus · Grafana · CloudWatch

Event-Driven Architecture

6 Kafka topics with MAWANI/ZATCA compliance-driven retention (14 days for cargo manifests, 30 days for customs declarations). SASL/SCRAM-512 encryption. Customer-managed KMS key. Consumer lag alerting at 1,000 messages.

Kafka MSK · SASL · KMS

Kubernetes-Native Deployment

EKS with HPA (CPU 70% / Memory 80%), pod anti-affinity across 3 nodes, zero-downtime rolling updates (maxUnavailable=0), ALB Ingress with SSL redirect. Secrets from AWS Secrets Manager. 30-day auto-rotation Lambda.

EKS · HPA · Secrets Manager

Infrastructure as Code

Complete Terraform for VPC, RDS Multi-AZ, ElastiCache, MSK, EKS in me-south-1 (Bahrain) for Saudi PDPL compliance. S3 remote state + DynamoDB locking. 5-stage CI/CD pipeline with ECR vulnerability scanning.

Terraform · AWS · GitHub Actions
Technology Stack

Enterprise-Grade Components

Every technology chosen for production reliability in a 24/7 port operations environment.

Python 3.12

3.12 LTS

Async-first runtime. asyncio + asyncpg for non-blocking I/O across all DB, Redis, and Kafka calls.

FastAPI 0.115

0.115

Pydantic v2 schema validation on every request/response. Auto-generated OpenAPI. Async route handlers throughout.

React 18

18 + TypeScript

Stakeholder portals: shipping agent dashboard, customs queue, container tracker. React Query for server state.

PostgreSQL 16

16 on RDS Multi-AZ

ACID transactions for vessel calls and manifests. Separate pcs_audit database with immutability trigger + INSERT-only role.

Redis 7

7 on ElastiCache

JWT blacklist, API key lookup, rate limit counters, AIS position cache, ZATCA OAuth2 token cache.

Apache Kafka 3.6

3.6 on Amazon MSK

6 compliance-driven topics. SASL/SCRAM-512. Customer KMS. auto.create.topics.enable=false in production.

Kubernetes (EKS)

EKS me-south-1

3-node cluster, HPA, ALB Ingress, pod anti-affinity. Zero-downtime rolling updates. Helm for monitoring stack.

Terraform 1.7

1.7 + S3 state

4 modules: vpc → rds → redis → kafka. S3 remote state + DynamoDB lock. Bahrain region for PDPL compliance.

Prometheus + Grafana

kube-prometheus-stack

15s scrape interval, 30-day retention, 12 alert rules, 11-panel dashboard with Saudi port domain context.

Developer Experience

Up and Running in 5 Commands

The full stack — PostgreSQL, Redis, Kafka, and FastAPI — runs locally via Docker Compose. Saudi port seed data included.

Clone & Configure

Copy the two environment files and fill in values (defaults work for local development).

Start All Services

Docker Compose starts PostgreSQL 16, Redis 7, Zookeeper, Kafka, Kafka UI, and FastAPI — all with health checks.

Run Migrations

Alembic creates all tables: vessel_calls, cargo_manifests, cargo_items, users, stakeholders, container_events, notifications.

Load Saudi Port Seed Data

Inserts 5 stakeholders, 7 users, 10 vessel calls (all statuses), 3 cargo manifests, 7 containers including dangerous goods and reefer.

Open API Docs

FastAPI's interactive Swagger UI at port 8000. Log in as orient_admin with password DevP@ss2024! to try all endpoints.

bash — pcs-port-system
# 1. Clone and configure $ git clone https://github.com/samiulAsumel/pcs-port-system.git $ cd pcs-port-system $ cp .env.example .env && cp backend/.env.example backend/.env # 2. Start all services $ docker compose up --build -d ✓ postgres healthy ✓ redis healthy ✓ kafka healthy ✓ pcs-api healthy # 3. Run database migrations $ docker compose exec pcs-api alembic upgrade head Running upgrade ... 003_notifications # 4. Load Saudi port seed data $ docker compose exec pcs-api python scripts/seed_dev_data.py ✓ 5 stakeholders ✓ 7 users ✓ 10 vessel calls ✓ 3 cargo manifests ✓ 7 containers # 5. Open API documentation $ open http://localhost:8000/docs # Kafka UI: http://localhost:8080
Local Service URLs
🐍 :8000/docs FastAPI Swagger UI
🟨 :8080 Kafka UI
🐘 :5432 PostgreSQL (pcs_app)
🔴 :6379 Redis
:8000/health Health check
API Reference

REST Endpoints

All endpoints require authentication (JWT Bearer token or X-API-Key header). Production base URL: https://api.pcs.portauthority.sa

POST /api/v1/auth/login Authenticate and receive JWT tokens

Rate limited to 5 requests/minute per IP. Account locks after 5 consecutive failures (15-minute lockout).

curl
curl -X POST https://api.pcs.portauthority.sa/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"username": "orient_agent_1", "password": "Str0ng!Pass#2024"}'
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600,
"user": { "role": "AGENT", "stakeholder_id": "11000000-..." }
401invalid_credentials: wrong username or password.
🔒
423account_locked: 5 consecutive failures. Includes retry_after_seconds.
429rate_limit_exceeded: 5 attempts/minute/IP.
POST /api/v1/vessels/ Register pre-arrival notification

Roles: AGENT (own stakeholder), OPERATOR, ADMIN. ETA must be ≥ 24 hours from submission. MAWANI requires 72 hours for vessels over 10,000 GT.

curl — Vessel: AL-KHAFJI · IMO: 9876543
curl -X POST .../api/v1/vessels/ \ -H "X-API-Key: pcs_a3f8b2c1..." \ -d '{ "imo_number": "9876543", "vessel_name": "AL-KHAFJI", "gross_tonnage": 52000, "eta": "2026-06-15T06:00:00Z", "purpose": "IMPORT", "shipping_agent_id": "11000000-..." }'
"id": "22000000-0000-0000-0000-000000000099",
"status": "EXPECTED",
"ata": null, "berth_assigned": null
422validation_error: IMO must be exactly 7 digits. ETD must follow ETA.
409duplicate_vessel_call: active call already exists for this IMO + voyage.
GET /api/v1/containers/{container_number} Track container status — 60 req/min per IP

Container number must be ISO 6346: 3 owner letters + category (U/J/Z) + 6 serial digits + 1 check digit. Validated at middleware layer.

curl — Container: TCKU3953430
curl .../api/v1/containers/TCKU3953430 \ -H "X-API-Key: pcs_a3f8b2c1..."
"container_number": "TCKU3953430",
"current_status": "CUSTOMS_CLEARED",
"location": "Block C, Row 14, Tier 2",
"consignee_name": "Al-Jazirah Electronics Co.",
"vessel_name": "AL-KHAFJI"
POST /api/v1/customs/submit/{manifest_id} Submit manifest to ZATCA Fasah — 20 req/min per stakeholder
Idempotent — safe to call multiple times. If a ZATCA reference already exists in Redis, it is returned without a duplicate submission.

Manifest must be in ACCEPTED status. Triggers OAuth2 token fetch (cached in Redis), builds ZATCA declaration, and submits to Fasah API.

"zatca_reference": "SADAN-PCS-MAN-2026-0099",
"customs_status": "PENDING",
"submitted_at": "2026-06-12T09:30:00Z"
POST /api/v1/auth/refresh Exchange refresh token for new access token

Send the refresh token issued at login. Refresh tokens have a 7-day TTL.

{ "refresh_token": "eyJhbGci..." }
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer", "expires_in": 3600
POST /api/v1/auth/logout Revoke current access token (JTI blacklisted in Redis)

No request body. Send only the Authorization: Bearer <token> header. The JTI claim is extracted and blacklisted in Redis.

204 No Content — token successfully revoked. The JTI entry in Redis auto-expires when the token would have expired naturally.
GET /api/v1/vessels/?status=EXPECTED&page=1&size=20 Paginated vessel call list with filters

status: EXPECTED | AT_ANCHORAGE | BERTHED | DEPARTED | CANCELLED
date_from / date_to: ISO 8601 UTC
page: 1-based (default 1) · size: 1–100 (default 20)

"items": [ /* VesselCallResponse[] */ ],
"total": 47, "page": 1, "pages": 3
GET /api/v1/vessels/upcoming Next 48-hour arrivals — Redis cached 60s

Returns EXPECTED and AT_ANCHORAGE vessels with ETA in the next 48 hours, sorted by ETA ascending. Results Redis-cached for 60 seconds.

[ {
  "vessel_name": "AL-KHAFJI",
  "status": "EXPECTED",
  "eta": "2026-06-15T06:00:00Z"
}, ... ]
PATCH /api/v1/vessels/{id}/status Status transition — OPERATOR / ADMIN only
{ "status": "AT_ANCHORAGE",
  "timestamp": "2026-06-15T08:22:00Z",
  "notes": "Pilot boarding confirmed." }
EXPECTED     → AT_ANCHORAGE, CANCELLED
AT_ANCHORAGE → BERTHED, CANCELLED
BERTHED     → DEPARTED, CANCELLED
DEPARTED    → no further transitions
422invalid_transition: e.g., BERTHED → EXPECTED. Response includes valid_transitions[].
🔒
403forbidden: only OPERATOR and ADMIN roles may update vessel status.
GET /api/v1/customs/status/{manifest_id} Live clearance status from ZATCA Fasah
Fetches live status from ZATCA (not cached) and updates the local record if status has changed. If CLEARED, containers are released automatically.
"customs_status": "CLEARED",
"zatca_reference": "SADAN-PCS-MAN-2026-0099",
"duties_assessed_sar": 4250.00,
"last_checked_at": "2026-06-16T11:34:00Z"
Complete API reference with EDIFACT CUSCAR mapping, HS code rules, dangerous goods requirements, and integration guide → API_DOCUMENTATION.md
Release History

Changelog

Format follows Keep a Changelog. Versioning follows Semantic Versioning.

v1.0.0 2026-05-29

Initial Release — Full Stack

  • infraTerraform VPC, RDS Multi-AZ, ElastiCache, MSK, EKS in me-south-1 — Saudi PDPL data residency
  • featVessel Call API: EXPECTED → AT_ANCHORAGE → BERTHED → DEPARTED lifecycle with MAWANI 72h validation
  • featCargo Manifest API: EDIFACT CUSCAR mapping, ISO 6346 validation, IMDG dangerous goods, reefer temperature
  • featZATCA Fasah integration: OAuth2 client credentials, declaration submission, HMAC webhook verification
  • featAIS vessel tracking: MarineTraffic / Spire feed, 15-minute polling, ETA deviation alerting
  • featContainer tracking: full ISO 6346 lifecycle from MANIFESTED to DELIVERED with webhooks
  • secZero-trust security: HSTS, CSP, SQL injection middleware, slowapi rate limiting, immutable audit log
  • featPrometheus monitoring: 10 custom metrics, 12 alert rules, 11-panel Grafana dashboard
  • infraGitHub Actions: 5-stage CI/CD — lint → test → ECR scan → staging → [approval] → production
  • docs1,400-line API documentation with EDIFACT mapping, HS code rules, and full integration guide
v1.1.0 Planned — Week 7

AIS Live Map + Notifications

  • featAIS live vessel position map (WebSocket streaming, Leaflet.js)
  • featUnifonic SMS/WhatsApp notifications for consignee pickup alerts
  • featZATCA batch sync Lambda for overnight declaration catches
Open Source

Contributing to PCS

We welcome contributions from port technology engineers, FastAPI developers, and Saudi logistics domain experts.

🍴

1. Fork & Setup

Fork the repo, copy .env.example, run docker compose up. The full stack runs locally in under 5 minutes.

🧪

2. Write Tests First

Minimum 80% coverage enforced in CI. Use real SQLite test DB — never mock the database. pytest-asyncio for async routes.

🔍

3. Lint & Format

ruff check backend/ must pass with zero warnings. ESLint for React. No hardcoded env values or print() statements.

📝

4. Open a PR

Fill in the PR template including Port Operations Impact section. Changes to auth, audit, or ZATCA require 2 reviews.

📚

Port Domain Resources

New to port operations? Start with the IMO ship identification guide, ISO 6346 container numbering, and ZATCA Fasah developer portal.

💬

Questions?

Open a GitHub Discussion or email api-support@portauthority.sa.