aDSA is a free (GPLv3 + GCC Runtime Library Exception) Partition
Communication Subsystem for Ada’s Distributed Systems Annex (Annex E),
built on ZeroMQ — a runtime replacement for the deprecated PolyORB /
GLADE. The compiler side of Annex E is alive and well in FSF GNAT; aDSA
supplies the other half: System.RPC / System.Partition_Interface
implementations the generated stubs call into, plus pcs_gnatdist, a
gnatdist-style build tool that takes the classic .cfg configuration
language and produces the partition executables and a launcher.
It covers the full annex — RCI, RACW/RAS (including callbacks),
Shared_Passive (three backends: file, a cross-host store server, and
same-host shared memory), asynchronous calls, termination policies,
restart recovery, and the RM E.3 version check — plus opt-ins for zlib
compression, XDR wire format, and CurveZMQ encryption with per-partition
peer authentication. It is validated against the original PolyORB examples/dsa corpus.
New in 1.3.0 — the platform release:
Windows 11: full bring-up with the Alire toolchain (gnat_native
15.2.1 + gprbuild 26.0.1, Alire’s bundled MSYS2 bash). The verification
corpus passes 6/6 on both build paths — the same scoreboard as Linux —
and all three Shared_Passive backends run, including shared memory.
FreeBSD 15.1: corpus plus the full feature sweep (XDR, peer-auth,
and a real two-machine cross-host deployment).
pcs_gnatdist now lints RCI specs against the RM E.2.3 legality rules
up front, instead of letting violations surface as puzzling linker
errors.
Assorted fixes — details in the changelog.
Linux remains the primary platform (Arch users get a PKGBUILD whose
pacman hook rebuilds the runtime automatically on every gcc-ada
upgrade).
One known FSF GNAT limitation worth mentioning: dispatching to an RACW
of a Remote_Types interface can pick the wrong slot when the concrete
type declares its own primitives before its overrides (filed as PR
ada/126014, reproducer included in the repo). The documented workaround
is to route such calls through a concrete RCI.
Is there a recommended way to detect if a partition has already started? So it is possible to gracefully shutdown a partition if it is already started.
I also wonder about encryption. I looked for an example where peer authentication on partitions is enabled but could not see an example in the examples/ directory. Or am I mistaken? In the documentation it says " DSA_AUTH=peer + DSA_NS_CURVE_*", how should one interpret DSA_NS_CURVE_* ? Should each partition have its own DSA_NS_CURVE_<partition_name> or how should one use the DSA_NS_CURVE_* environment variables?
Thanks — both of these were genuinely underdocumented, and you prompted me to
fix them, so this reply also comes with runnable code and clearer docs on the main branch.
Encryption / DSA_NS_CURVE_*. You read the notation the reasonable way, and
it was ambiguous — sorry. The * is just shorthand for the two suffixes SECRET and PUBLIC; it is not a per-partition placeholder. There is
no DSA_NS_CURVE_<partition>, and you never configure a key per partition by
hand.
There are two separate models:
Shared system key (DSA_CURVE_SECRET / DSA_CURVE_PUBLIC): a single
keypair for the whole deployment — secret on binders, public on connectors;
in practice set both on every process. Encrypts all traffic, but any holder
of the pair is trusted. Generate with eval "$(pcs_keygen)".
Per-partition mutual authentication (DSA_AUTH=peer): here DSA_NS_CURVE_SECRET/DSA_NS_CURVE_PUBLIC is the infrastructure keypair —
one pair for the entire deployment, generated with pcs_keygen ns.
Distribute it like this:
DSA_NS_CURVE_SECRET → the name server (and pcs_dsm, if used) only;
it becomes the trust anchor.
DSA_NS_CURVE_PUBLIC → every process (all partitions and the name
server), which pin the name server’s key.
DSA_AUTH=peer → every process as well.
Each partition generates its own keypair automatically at startup —
nothing to configure. It registers its public key with the name server, and
partitions authenticate one another by asking the name server whether a
presented key is a registered partition’s (a ZAP handler + an internal KEYOK check). An unregistered identity is rejected at the handshake.
On one host you can eval "$(pcs_keygen ns)" once and launch everything from
that shell (each process reads only what it needs). Across hosts: secret only
on the name-server host, public everywhere.
You were also right that there was no runnable peer-auth example — only rogue.adb. There’s now a complete one: examples/peer-auth/
(run_peer_auth.sh) — it sets up the infra key, runs a legitimate registered
client that succeeds, then fires the rogue with a fresh unregistered identity,
which is denied at the ZAP handshake. The DSA_NS_CURVE_* explanation above is
now written up in docs/users-guide.md §9 (“Encryption in practice”). Both
are on main.
Detecting an already-started partition. There’s no turnkey “singleton /
refuse-second-instance” feature today, but the pieces are there:
Detection: the name server is the registry — every partition registers
its units on startup. So “is a partition hosting unit X already up?” is
answerable by resolving X. From Ada, System.Partition_Interface.Get_Active_Partition_ID ("X") returns the hosting
partition’s id if one is running, and raises Communication_Error if not —
you can call that in your main before handing off to Run.
What happens if you start a second instance today: both register; the name
server’s unit→partition mapping is latest-wins, so the new instance
transparently takes over and callers re-resolve to it (the restart-recovery
path). It does not error or refuse.
Gracefully shutting the existing one down: the mechanism exists — the name
server sends a one-byte 'X' shutdown frame to a partition’s control endpoint
(that’s how global termination and the liveness watchdog stop partitions) —
but it isn’t yet exposed as a targeted “shut down partition N” command. A small
name-server verb, or a one-line client that resolves the unit and sends 'X'
to its control endpoint, would give exactly the “gracefully replace the
already-started instance” behaviour you want.
This “detect and replace” pattern is really the job of a supervisor/orchestration
layer on top of the PCS, which I’ve just sketched as a design entry — see docs/developers-guide.md §16. Happy to go deeper on any of it.
Is it possible to use it with a process running on a PC (Windows or Linux) on one side and a process running on a ARM device (Linux) on the other side ?
The DSA_XDR=1 build flag should take care of any endian difference, as well as any type size differences (such as is possible with Long_Integer).
I have not tested for ARM, as yet, but will do so today and let you know the results.
I have run a test with 3 partitions across separate Windows, freeBSD and Linux VM’s successfully. The freeBSD box even used an older GCC/GNAT (v15) than on the other two (v16).
Just tested on ARM with Debian 13 and GCC/GNAT v14. All tests ran successfully, except that I was mistaken about XDR handling type size differences (such as Long_Integer). These aren’t handled by XDR but a warning is now given by gnatdist when such types are used.