Bidding Operating System

harga.

Find every tender. Price every line item. Submit before the deadline.
Four procurement portals. 19K price records. One pipeline.

-
Active Bids
Live pipeline across all entities
4
Portals
SmartGEP, ePerolehan, Etimad, Forsah
-
Entities
Consurv, Dyna-Segmen, Dyna-OM + test
8
Pipeline Stages
Tender → Bid → Scope → Price → Confidence → Package → Approval → Submit
Pipeline

8 deterministic stages.
Gate checks at every step.

Each stage has a quality gate that must pass before the bid advances. Gates prevent progression without the right data. The same pipeline drives CLI, TUI, and web console.

00 Discover entity != null Tenders from SmartGEP, ePerolehan, Etimad, Forsah. Auto-triage & entity assignment.
01 Bid deadline set Create bid record. Assign entity, capture scope, calculate deadlines.
02 Scope items > 0 Parse tender docs. Extract line items, specs, quantities via LLM + template fillers.
03 Price 80% coverage 19K-record price memory + RFQ supplier quotes. Falls back to LLM estimate.
04 Confidence score > 0.3 Win probability from price competitiveness, entity fit, history, risk factors.
05 Package gate_check Assemble technical proposal, financial schedule, compliance docs, SoftCert.
06 Approval signed off Approval request, review, digital signing with audit trail. No bypass.
07 Submit preflight ok Playwright portal submission. Outcome feedback loop to price_memory.
Infrastructure

16 services,
one venv, 3.5 GB total.

All sec services run under PM2 on a single machine. Shared virtualenv, shared sdk/, no Docker overhead. Each service has its own port, memory cap, and restart policy.

sec-harga-v9
:3647
mem 241 MBcap 3.0 GB
sec-tenders-api
:3646
mem 142 MBcap 1.0 GB
sec-scheduler
daemon
mem 49 MBcap 1.0 GB
sec-sched-api
:3642
mem 64 MBcap 512 MB
sec-enrich
daemon
mem 967 MBcap N/A
sec-proxy
:3641
mem 51 MBcap 256 MB
sec-products-api
:3644
mem 66 MBcap 512 MB
sec-guardian
(health)
mem 162 MBinterval 300s
sec-failsafe
:3643
mem 40 MBmax_restarts 99
sec-documents
daemon
mem 59 MBcap N/A
sec-analytics
daemon
mem 59 MBcap N/A
sec-webdav-intel
:3651
mem 67 MBcap 4.0 GB
sec-harga-v9-sched
daemon
mem 516 MBcap 3.0 GB
sec-agent
(arbos)
mem 107 MBcap 3.0 GB
sec-harga-bot
(arbos)
mem 107 MBcap 1.0 GB
Operations

Optimised for
reliability, not throughput.

Every parameter has a specific reason. These values were not guessed — they were arrived at through failure analysis.

PM2 Restart Policy exp_backoff_restart_delay: 10s Exponential backoff from 10s base. Prevents restart-storm on cascading failures. max_restarts 20–30 per service.
Kill Timeout 20–45s (per service) 45s for harga-v9 (LLM streaming can take 30s+). 20s for proxies and APIs. 90s for scheduler (task drain).
Memory Caps 256 MB → 4.0 GB 3.0 GB for harga-v9 (LLM spike buffer). 4.0 GB for webdav-intel (doc scans). Tight 256 MB for proxy, failsafe.
Stop Signal SIGTERM (most) PM2 default SIGINT translates to non-graceful SIGQUIT in gunicorn. harga-v9 uses SIGTERM + graceful_timeout=90s via when_ready hook.
Scheduler Concurrency global_max: 5, group_max: 3 Limits concurrent pipeline runs globally and per-portal. Prevents resource thrashing across SmartGEP / ePerolehan / Etimad.
LLM Circuit Breaker auto-open on errors Tracks LLM errors per provider. Opens circuit at threshold. Pricing falls back to price_memory when LLM is unavailable.
Guardian Interval 300s (5 min) Health check cycle. Alerts via dedicated Telegram bot. Never touches orkes processes — isolated monitoring plane.
Session Cleanup on startup + lazy Expired sessions purged on service start. Active sessions lazily evicted. HMAC tokens with 24h expiry.
Rate Limiting 1.5 RPS (Forsah) Per-portal rate limits via sdk/rate_limiter.py. Forsah: 1.5 RPS. SmartGEP: token carousel rotation. Etimad: 20-page batch cooldown.
Price Memory Cron Mon 05:17 UTC Weekly price memory seed from historical awards. 19K+ entries shared across yellowpages and harga.
Cache Headers no-store (API), immutable (dist) Cloudflare "Cache Everything" prevented by explicit no-store on API responses. Dist files get 1y immutable cache with content-hashed names.
Failsafe max_restarts 99 (unlimited) Failsafe must always be running — it recovers other services. Independent of all sec apps. Last resort recovery plane.
Data Layer

SQLite primary.
PG for analytics.

Two databases serve different purposes. harga_v8.db is the operational store. sec.db (Postgres) holds tender corpus and analytics.

harga_v8.db (SQLite)Bids, entities, users, workflow, approvals, RFQs, email, costing, analytics events, drop-ins, freight, CV records, seed history
sec.db (PostgreSQL)786 MB — tender corpus, buyer companies, supplier index (8.5K records), price research, document store
price_memory.db (SQLite)19K+ historical price records. Shared between yellowpages and harga via symlink. Weekly seed from awards.
Entity vault (SQLite)Encrypted credentials, portal accounts, API keys per bidding entity
Artifact gallery (SQLite)821+ artifacts (technical, corporate, financial, capability docs). File storage on NAS.
RedisSession cache, rate limiter counters, job sync state (optional, graceful degrade)
Security

Defence in depth.
No shortcuts.

AUTH

Cloudflare Basic Auth at edge. HMAC session tokens with 24h expiry. CSRF middleware on all mutation endpoints (X-Requested-With check). Beacon analytics exempted.

HEADERS

X-Content-Type-Options: nosniff, X-Frame-Options: DENY, X-XSS-Protection: 1; mode=block. No cookie-based auth — tokens in Authorization header only.

DOMAIN

harga.roowang.com via Cloudflare → Caddy → localhost:3647. Caddy terminates TLS. Cloudflare provides DDoS mitigation + IP filtering.

INTERNAL

V8_INTERNAL_SECRET for inter-service auth. All sec services share a venv but authenticate via HMAC. Failsafe runs on segregated recovery plane.

ENTITY

Credentials vault per bidding entity (Consurv, Dyna-Segmen, Dyna-OM). Portal passwords encrypted at rest. SoftCert PINs in accounts.json (gitignored).

SUBMIT

Dry-run default for ePerolehan submissions. Kill-switch file at data/eperolehan/eperolehan_submit.enabled. Value ceiling MYR 200K per bid.

CLI & TUI

harga-cli v0.3.0
50+ commands, 2 interfaces.

Resource-verb pattern. Every pipeline stage has a corresponding CLI command. Same SDK, same data — works over SSH, in tmux, or via ttyd in the browser.

DISCOVER

harga tender list — list/filter tenders by portal, status, entity. harga tender show <id> — full tender context.

SCOPE & PRICE

harga scope run — extract items from docs. harga price auto — auto-price from memory. harga price explain — pricing rationale.

BID LIFECYCLE

harga bid run --from stage --through stage — execute pipeline segment. harga bid resume — continue from last gate failure.

RFQ

harga rfq create/send/follow-up — supplier RFQ lifecycle. harga quote ingest/compare/accept — quote management.

APPROVAL

harga approval request/approve/reject/withdraw/list-pending — full approval workflow with audit trail.

TUI

harga ops dashboard — Textual Bloomberg-style TUI. 8-stage pipeline view. Keyboard-driven (1-8 filters, / command mode, r refresh).

Architecture

Three layers,
one constitution.

The bidding OS is a federation with a shared contract layer. Each component has a single responsibility and a well-defined interface.

01

Front Door

harga.work — the single entry point. Serves the web console and routes to the engine. Owns no data. Aggregates every product under one domain.

02

Engine

orkes_sec — all data, all business logic. 786 MB tender corpus, 19K pricing records, 548+ bids, full audit trail. Products call in; they don't own databases.

03

Company OS

orkes_os — the constitution. Typed vocabulary, transition graph, audit log, safety walls. Every product imports this. Prevents disagreement on what words mean.

Product Suite

Bidding is the core.
The ecosystem extends it.

harga.work is the primary product. Supporting products feed data in and close loops after submission.