Skip to content
rgs.plus

Architecture

One node per RTA. Nine containers. No way in from outside.

Registry is sold white-label. Each RTA client operates a dedicated node — its own server, its own database, its own object store, its own depository credentials, its own backups and its own encryption keys. No RTA’s register touches another’s, and none of it reaches the vendor’s console.

Fleet

The control plane cannot open a connection to a node.

There is no inbound port and no public address. The control plane leaves Ed25519-signed commands in a queue that the node collects on its own schedule — addressed to one node, with a nonce and an expiry, so replays and tampering are rejected and reported.

NODE — RTA ASTANDARD TIERown Postgres · own keysown object store · own backupsNODE — RTA BRESILIENT TIERown Postgres · own keysown object store · own backupsNODE — RTA CCRITICAL TIERown Postgres · own keysown object store · own backupsheartbeat 60soutbound mTLS onlycommand poll 30sno inbound pathCONTROL PLANEnode inventory · licences · claim tokenssigned command queue · release ringsHolds no register data. Ever.
Figure 1 — nodes are dedicated per RTA and talk outward only.

Node identity

An Ed25519 key pair generated on the machine and never transmitted, with a signed client certificate returned at enrolment.

Outbound-only telemetry

A heartbeat every sixty seconds carrying counts and states only, filtered through an allow-list applied immediately before send.

Consent-gated support

A reverse tunnel opens only when an operator at the RTA approves it on their own node — time-boxed and recorded.

Seventy-two hour offline grace

Full local operation if the control plane is unreachable, because the answer to what happens if the vendor disappears has to be nothing.

Inside a node

Nine containers, one compose file, started by a systemd unit.

A bootstrap script takes a claim token and brings a bare Ubuntu server to a running node: preflight, packages, layout, hardening, identity, enrolment, secrets, signed stack, start, activate. Every step checks whether it has already run, so a half-finished provision is fixed by running the same command again.

Services running on a node
ServiceResponsibility
edge (Caddy)TLS termination, security headers, access log
appThe Next.js console used by RTA staff, issuer users and investors
worker ×2BENPOS ingest, reconciliation, entitlement computation, TAT escalation, IEPF clock, OCR
depo-connectNSDL and CDSL SFTP pull, mailbox poll, watched drop folder
postgresThis RTA’s register. Data checksums on, WAL archived continuously
pgbouncerTransaction pooling
objects (MinIO)Scans, raw depository files, bank files, generated certificates
backupContinuous WAL archive, nightly full, weekly automated restore test
agentThe only component that communicates with the control plane

Ingest

Four gates, three intake paths, one parser per depository per file kind.

SFTP or FTPS pull on a schedule, IMAP mailbox poll, and a watched drop folder — because in practice all three get used. Parsers are versioned with golden-file tests. There is no shared parser that tries to be clever about both depositories.

PAN, bank account, email and mobile are hashed and masked in the ingest process. The raw values never reach the database.

Position fileBENPOS_INE0ABC01234_20260724.csvstored raw · hashed · registered as a depository_batch
  1. 1

    Gate 1Shape

    23 columns in order, plus the _meta sheet

  2. 2

    Gate 2Control totals

    SUM(qty) per depository = _meta counts

  3. 3

    Gate 3Row integrity

    No duplicate account keys · no negatives · pledged + locked ≤ total

  4. 4

    Gate 4Continuity

    Δ NSDL + Δ CDSL = 0, or a register event explains it

Figure 2 — a position file from arrival to commitment. Choose a file to run it.

Deliberate choices

Each of these was a decision, and each has a reason you can argue with.

Server components for every list and dashboard

These screens are dense tables of server-owned data with no client state worth shipping. The work queue’s filter and selection is the one place a client component earns its keep.

PostgreSQL does the heavy lifting

Reconciliation is a query, not application code. Position and audit tables are partitioned by month; a mid-size issuer generates around forty thousand position rows a week and eight years of them are retained.

Jobs in the database, not an external queue

A job must be transactional with the register change it causes, and an auditor will ask what ran when. Claimed with SELECT … FOR UPDATE SKIP LOCKED.

Row-level security rather than application filtering

A query that forgets its WHERE clause still returns nothing. This is the difference between a platform you can sell to two RTAs and one you cannot.

Maker–checker as a database constraint

Policy expressed as code is auditable. Policy expressed in a user interface is not.

NUMERIC for money and quantities

Never floating point. Fractional entitlements are real — bonus rounding and splits produce them.

Raw files kept forever

When a depository disputes a figure two years later, the file you received is the argument.

Redundancy

Automatic failover is deliberately not the default.

Patroni gives automatic promotion and also split-brain risk on a two-node pair. For a share register a ten-minute human-confirmed promotion is usually the better trade. A failover that has never been rehearsed is not a failover, so the top tier carries a contractual quarterly drill.

Standard

Single node, continuous WAL archive, nightly full, offsite encrypted copy

RPO
~5 min
RTO
2–4 h

Resilient

Default

Primary plus hot standby in the same datacentre, streaming replication, manual promotion

RPO
~0
RTO
10–15 min

Critical

Resilient plus an asynchronous DR node in a second Indian region, quarterly failover drill

RPO
~0 local, ~15 min regional
RTO
~30 min regional

Integrations

Every one of these is a file or an API whose schema changes without much notice.

Each is built as a versioned adapter with golden-file tests, and the raw payload is retained.

One operational detail that will bite you

Depositories whitelist source IP addresses. Each node needs a static egress address and a process for registering it. Putting nodes behind a shared NAT loses the isolation story and tangles the whitelisting at the same time.

External counterparties
CounterpartyWhat flowsMechanism
NSDLBENPOS, demat confirmations, remat, IDT, corporate action creditsSFTP or FTPS pull
CDSLThe same, with distinct file layoutsSFTP or FTPS pull
ExchangesShareholding pattern, listing certificatesPortal upload
MCA and IEPF AuthorityIEPF-2, IEPF-4, IEPF-5 verification reportsPrescribed forms
SEBI SCORESComplaint intake and action-taken reportsPortal or API
ODR institutionsDispute referral and outcomePortal
PAN validationPAN status and Aadhaar linkageAPI
KRA or CVLKYC records for compliant holdersAPI
Penny-drop providerBank account validationAPI
Bank host-to-hostDividend payment file, UTR reconciliation, returnsSFTP, two-person release
E-voting agencyRegister snapshot at cut-offFile
CAS providerConsolidated account statement feedFile

Want the provisioning walkthrough?

We will take a bare server to a running node on a call, and you keep the keys at the end of it.

Book a demo