Skip to content

Releases: Gautam-aman/ResQNet

v0.2.0-beta — Android Experimental Preview

Choose a tag to compare

@github-actions github-actions released this 20 Oct 08:08

ResQNet v0.2.0-beta — Android Experimental Preview

⚠️ Security Notice — October 26, 2026

A security issue has been identified in the Android APK distributed with ResQNet v0.2.0-beta.

Please do not download, install, or use this APK. If you've already installed it, stop using it until a corrected version is available.

We're preparing v0.2.1-beta, which includes security fixes and additional automated testing. The updated build is currently undergoing final review.

ResQNet remains experimental research software. Real-device mesh communication has not yet been verified, and it must not be relied upon for emergency communication.

We'll update this notice when the corrected release is published.

Thank you for your understanding.

Experimental pre-release

This is the first release that ships a downloadable Android APK. Everything else
in it — the mesh engine, the simulator, the coordination gateway, the command
centre — has been present since v0.1.0-alpha and is unchanged in kind.

Read this first. This is research software distributed as a pre-release.
It is not a replacement for official emergency services and must not be
relied upon as one. Do not deploy it where a failure would cost lives.


What's new since v0.1.0-alpha

The Android APK

The headline change. dev.resqnet.android, signed with a release keystore and
verified with apksigner, installable on any device running Android 8.0 (API
26) or newer.

Alongside it, four defects were found by auditing the Android application and
fixed. Each of them would have broken a multi-device demonstration on its own:

  • SOS processing. The two welcome-screen buttons performed the same action,
    so the path into composing a message was not the path the label described.
  • Peer identity rebinding. Nearby Connections issues a fresh endpoint id
    every time a link comes back up. The provisional binding made on
    reconnection was stored under a handle that never appeared in the mapping,
    while the mapping still pointed at the handle from the link that had just
    died. Directed messages addressed to a peer this device already trusted were
    sent to an endpoint that no longer existed, and nothing looked wrong: the peer
    was still listed and the trust store still held its key.
  • Retry behaviour while isolated. A message queued on a relay with no peers
    was charged against the retry budget for every no-op pass, so a device that
    had been alone all night recorded someone's SOS as "gave up after 12
    attempts" without a single transmission having been attempted.
  • Hop accounting at a relay. A relay that held a frame for a later pass
    stored the copy it had intended to send, which had already spent a hop. The
    frame was charged for a hop the radio never accepted, the retry spent the same
    hop again, and a two-hop path arrived at its recipient reported as three. A
    relay holding a frame under a tight budget could declare it hop-exhausted
    before it had left the device once.

Instrumented Android testing in CI

connectedDebugAndroidTest now runs in GitHub Actions against an emulator at
320×640 @ 160 dpi: SOS flow, permission flow, identity handling, message
processing and the regression tests for the defects above. 33 tests. The same
suite has also been run locally at 1080×2400 @ 420 dpi.

Everything else

  • The mesh core now has 154 tests, the simulator 51, the gateway 60, the Android
    unit suite 56, and the web suite 46.
  • The release workflow builds, verifies and publishes the signed APK. It still
    refuses to run when signing material is committed to the tree, still defaults
    to a dry run, and still cannot be triggered by a push.
  • The README was restructured and four claims that no longer matched the source
    were corrected.

Hardware testing

The developer reports the Android application working on two physical
phones.

That is a user report. It is not independently verified evidence, and it is not
evidence of multi-hop routing. With two handsets in range of each other,
Nearby's P2P_CLUSTER strategy connects them directly — there is no third
device to relay through, so no hop is taken and no relay participates.

What is not verified:

  • End-to-end offline delivery between three or more physical devices.
  • Cryptographic acknowledgement over real radios.
  • Physical radio range or penetration.
  • Battery behaviour under continuous mesh participation.

A proper multi-device run requires the phones to be out of range of one another
so that a relay actually exists. The procedure is
apps/android/DEVICE_TESTING.md, and no result from it is recorded.

Every other claim in this release rests on automated tests, emulator runs, and
a deterministic simulator. The simulation results in docs/benchmarks/results.md
describe the routing engine against a modelled radio and are not field
measurements.


Known limitations

  • Experimental research software. No certified reliability guarantee of any
    kind.
  • Not a substitute for emergency services.
  • Three-device multi-hop routing is unverified on physical hardware.
  • Real-world radio range and battery behaviour are unknown.
  • Multi-emulator testing has not been performed.
  • Android background restrictions may suspend the mesh. Doze mode, battery
    optimisation and manufacturer task killers can stop the foreground service.
  • Payloads are signed but not encrypted. Envelope signatures guarantee
    authenticity and integrity, not confidentiality. Any device in radio range
    that receives a frame can read it.
  • Peer-identity rebinding is not proven by a challenge-response. Rebinding a
    known identity to a new radio endpoint relies on the claimant presenting a
    public key already on file. A public key is public, so a device that has met
    the victim can rebind the victim's identity to its own endpoint and black-hole
    or read traffic addressed to them. It cannot forge frames as the victim.
    Closing this needs a link-bound challenge-response, which is not implemented.
  • No P2P_CLUSTER connection-level authentication token. Trust is enforced
    one layer up, by envelope signature verification against a key the owner
    confirmed in person. That is the stronger property, but an unauthenticated
    peer can still occupy a connection slot.
  • The shared gateway token is a bearer secret. Revocable by rotation, but not
    attributable; every device holding it shares one identity.
  • PostgreSQL under concurrent writes and replication is untested.
    Migrations and the incident lifecycle are tested.
  • Incident assignment and manual incident creation have working endpoints but
    no UI.

Installation

Android

  1. Download resqnet-v0.2.0-beta-release.apk from this release.
  2. Verify its SHA-256 against SHA256SUMS before installing.
  3. When Android prompts, allow installation from the source you downloaded it
    from. Only ever do this for an APK you obtained from this repository's
    releases page.
  4. Install the application.
  5. Grant the nearby-devices permission when asked.
  6. Enable mesh participation explicitly. It is a deliberate choice, not a
    default.
  7. Test communication with a second device.

Do not install resqnet-v0.2.0-beta-debug.apk. It is included for side-by-side
hardware testing only; it carries the .debug application-id suffix and is
debug-signed.

Simulator, gateway and command centre

Unchanged from v0.1.0-alpha and covered in full in INSTALLATION.md beside this
file. Prerequisites are Java 21+, PostgreSQL 14+, and Node.js 21+ to rebuild the
web bundle.


Contributing

Issues and pull requests are welcome. When you report a finding, say honestly
whether you reproduced it, read it, or inferred it — several defects
in this codebase were found by reading it rather than by running it, and the
difference matters to whoever triages it.

If you have tested the Android app on real hardware, that result is worth more
than anything else you could send. See apps/android/DEVICE_TESTING.md.

License

MIT. See the repository root.

What's Changed

  • Android automation: emulator instrumentation suite, two confirmed defect fixes, bounded CI by @Gautam-aman in #1

Full Changelog: v0.1.0-alpha...v0.2.0-beta

v0.1.0-alpha — Experimental Developer Preview

Choose a tag to compare

@github-actions github-actions released this 18 Oct 19:27

ResQNet v0.1.0-alpha

Experimental Developer Preview

ResQNet is an experimental offline-first emergency communication platform
exploring decentralized messaging, multi-hop routing, store-and-forward
delivery, and incident coordination.

Read this first. This is an early developer preview of research
software. It has never been run between two physical Android phones. It is
not a replacement for official emergency services, and it must not be
relied upon as one. Do not deploy it where a failure would cost lives.


Included

  • Java-based distributed mesh routing engine
  • Deterministic network simulator
  • Reproducible multi-hop routing demonstrations
  • Spring Boot emergency coordination backend
  • PostgreSQL persistence with versioned migrations
  • React emergency command center
  • Real-time gateway integration over WebSocket
  • Automated testing and CI

Technical highlights

Peer identity

Every device holds an Ed25519 key pair generated on first launch. Its stable
identifier is rqn1_ followed by a truncated SHA-256 fingerprint of the public
key, so a device keeps the same identity across restarts and reinstalls of
application state. Messages are signed end to end; a relay forwards an opaque
envelope it has no means to read, and an intermediate device is never shown a
message body it merely carried.

Peer trust is a human act. A key is unverified until a person compares a short
code on two devices, and messages from an unverified peer are refused with an
UNTRUSTED_ORIGIN reason rather than silently dropped.

Controlled flooding and deduplication

Delivery is flooding, not routing: a node rebroadcasts an envelope it has not
seen before. Two bounds keep that from being quadratic. Split horizon suppresses
retransmission back toward the node the copy arrived from, and every envelope
carries a message ID that is remembered for a bounded window, so duplicates are
dropped rather than amplified.

ClusterTopologyTest asserts the consequence that matters: in a fully
connected cluster a message reaches a third device in one hop and no relay
participates, while in a chain topology the same message crosses the relay in
two hops without the relay displaying its body.

Message acknowledgments

A message is only reported delivered once the origin receives an end-to-end
acknowledgment from the destination. Handing a message to a nearby device and
having it confirmed are distinct states in the UI, because the difference
between "I gave it to someone" and "someone received it" is the difference
between a working queue and a false promise.

Offline message persistence

Messages that cannot be routed are held in a durable queue and retried on a
timer within their lifetime, rather than failed. The queue survives process
death and app restart. A message that outlives its lifetime expires as too old
to send
— it is never marked delivered.

Routing behavior under network partitions

docs/benchmarks/results.md records the same seed run twice, once partitioned
and once healed. Messages queued during the partition are delivered after it
heals. This is the property store-and-forward exists to provide, and it is
verified in simulation.

Measured simulation performance

From docs/benchmarks/results.md, seed 42, 8 CPUs, JVM 21.0.7:

Nodes Delivered Median p95 Amplification Forwards Dupes dropped
10 69 375 ms 375 ms 8.7× 920 1 941
50 237 375 ms 375 ms 7.9× 868 22 323
100 593 375 ms 375 ms 7.2× 789 51 228
500 3 704 500 ms 625 ms 4.6× 691 239 558

These are simulation results, not field measurements. They describe the
routing engine against a modelled radio. Nothing here is a measurement of
Nearby Connections, Wi-Fi Direct range, real latency through rubble, or real
battery life. The delivery figures describe a simulation whose message
lifetime and retry windows were chosen by the author.

Known limitations

  • Physical Android networking has not been tested. The app has never been
    run on hardware. No two phones have ever exchanged a message.
  • No Android APK is included in this release, for that reason. The Android
    source is in the repository and the build is reproducible; binaries will follow
    once apps/android/DEVICE_TESTING.md has been executed and its results
    recorded.
  • Simulator results are not real-world radio measurements. Every number
    above comes from a discrete-event model.
  • Real-world communication range and battery impact are unknown. Nothing in
    this repository is evidence about either.
  • No load, soak, or battery testing has been performed.
  • The simulator cannot publish to the backend. SimulatorCli --gateway
    exists but does not work: it posts a run summary, while the /api/v1/gateway/sync
    endpoint expects per-message records. The endpoint is answered and validated
    correctly — the sender is what is unfinished. The dashboard's live event
    stream over WebSocket works independently of this.
  • The shared gateway token is a bearer secret: revocable by rotation, but not
    attributable, and every device holding it shares one identity.
  • PostgreSQL under concurrent writes and replication is untested. Migrations
    and the incident lifecycle are tested.
  • Known defects found by reading this code were fixed, but fixing them is not
    evidence that the system works.

This software is experimental. It must not be relied upon as a replacement
for official emergency communication services.

Getting started

Prerequisites: Java 21 or newer, Maven 3.9+, PostgreSQL 14+, and
Node.js 21+ for the web dashboard.

1. Run the simulator

No infrastructure required.

java -cp resqnet-simulator-v0.1.0-alpha.jar \
  dev.resqnet.mesh.simulator.demo.DemoScenario

A seven-stage demonstration. It prints a warning that it is a simulated radio,
runs each stage, and exits non-zero if any stage misbehaves.

A single seeded simulation:

java -jar resqnet-simulator-v0.1.0-alpha.jar \
  --topology mesh --nodes 12 --messages 40 --seed 7 --rounds 60 --quiet

2. Start PostgreSQL

createdb resqnet
psql -c "CREATE USER resqnet WITH PASSWORD 'choose-a-real-password';"
psql -c "GRANT ALL ON DATABASE resqnet TO resqnet;"

3. Launch the backend

RESQNET_JWT_SECRET is mandatory: without it the backend refuses to start,
because responder sessions would otherwise be signed with a publicly known key.
RESQNET_GATEWAY_TOKEN is not needed to start, but without it every device sync
is refused with 401, so devices cannot report. Set both, and do not reuse the
values shown.

export RESQNET_DB_URL=jdbc:postgresql://localhost:5432/resqnet
export RESQNET_DB_USER=resqnet
export RESQNET_DB_PASSWORD='choose-a-real-password'
export RESQNET_JWT_SECRET=$(openssl rand -base64 48)
export RESQNET_GATEWAY_TOKEN=$(openssl rand -hex 24)

java -jar resqnet-backend-v0.1.0-alpha.jar --spring.profiles.active=dev

The dev profile seeds two demonstration accounts and switches on the API
documentation. It is for local evaluation. Never deploy with it active: the
seeded accounts use a password printed in the source, and the schema becomes
publicly readable. A production database gets its first account from a migration
run by a human, because there is no self-service signup and that is
deliberate.

It listens on port 8080. Flyway applies the schema migration on first start;
Hibernate validates rather than mutates the schema. There is no bundled H2
driver — H2 is a test-scope dependency, and pointing the jar at H2 fails at
startup by design.

Check it:

curl -fsS http://localhost:8080/actuator/health   # {"status":"UP"}

The OpenAPI schema and Swagger UI are disabled by default and are published
only under the dev profile, because an exposed schema tells an attacker what
exists without any probing.

4. Serve the web dashboard

The dashboard uses same-origin relative URLs (/api/v1/... and
/ws/incidents), so it needs a reverse proxy that forwards those two paths to
the backend.

unzip resqnet-web-v0.1.0-alpha.zip -d resqnet-web
server {
    listen 80;
    server_name localhost;

    root /srv/resqnet-web;
    index index.html;
    try_files $uri $uri/ /index.html;   # client-side routing

    location /api/ {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /ws/ {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}

Serve this over HTTPS in any real deployment: the backend defaults
RESQNET_REQUIRE_HTTPS=true.

The WebSocket is authenticated through the subprotocol channel rather than a
header, because a browser cannot set an Authorization header on a socket. The
client negotiates resqnet.v1 and carries the session token as a second
protocol token prefixed bearer.. A connection without it is closed during the
handshake, before it can subscribe.

5. Open the command center

Browse to http://localhost/ and sign in as responder / resqnet-dev. The
dashboard then live-updates as incidents change.

6. Run the demonstration

The seven-stage demonstration needs nothing running. It composes an SOS with no
network, delivers it through one relay, drops the relay mid-flight, requeues and
delivers on recovery, shows that a relay never displays a body it carried,
distinguishes end-to-end acknowledgement from one successful hop, and drives a
responder triage. It prints its own warning that the radio is simulated, and
exits non-zero if any stage misbehaves.

java -cp resqnet-simulator-v0.1.0-alpha.jar \
  dev.resqnet.mesh.simulator.demo.DemoScenario

Contributing

Issu...

Read more