Bringing MyZubster to the Tor Network: Building an Onion Service with Docker, React and Node.js
Building a privacy-conscious access layer for an open-source ecosystem — with real tests, reproducible bugs, and contributions from the community.
The next chapter of MyZubster is taking shape.
We're working toward making MyZubster accessible through the Tor network using a .onion address, alongside our traditional website.
This is not just about adding Tor to a Docker container. It's about understanding how an Onion Service interacts with an existing application stack, how we protect its identity, and how we make the deployment reliable.
And we're building it openly, with community contributors.
Meet the contributors
Two people deserve particular recognition for the work we're currently reviewing and validating.
Mohammed Wasim Khan — GitHub: @wasim-builds
Wasim has been working on a pull request that brings together improvements to Docker infrastructure, backend services, frontend configuration, and the Tor integration.
His current contribution is available here:
MyZubster PR #1582 — Wasim's contribution
Nicola — N4K48
Nicola's independent pilot and earlier contribution form an important part of the work that is now being reviewed and extended.
You can explore the original contribution here:
MyZubster PR #1457 — Nicola / N4K48
This is the kind of open-source collaboration we want to encourage: independent experiments, code contributions, technical review, reproducible tests, and improvements that can eventually benefit the wider ecosystem.
What are we building?
The objective is to provide a second access path to MyZubster through a Tor Onion Service.
The proposed architecture looks like this:
TRADITIONAL WEB
|
MyZubster
|
Nginx
|
v
+----------------+
| React Frontend |
+----------------+
|
v
+----------------+
| Node.js API |
+----------------+
|
v
+----------------+
| MongoDB |
+----------------+
TOR NETWORK
|
Tor Browser
|
.onion address
|
+----------------+
| Tor Container |
| Hidden Service |
+----------------+
|
Docker Network
|
v
+----------------+
| React Frontend |
| Nginx |
+----------------+
|
v
Backend API
The concept is simple:
The Onion Service is an alternative entry point, not a replacement for the application.
The backend and database do not need to expose public ports just because users can reach the frontend over Tor.
That separation is an important part of the deployment design.
Technology stack
The components under test include:
- Docker and Docker Compose for service isolation.
- Tor on Debian Bookworm.
- React for the frontend.
- Nginx for serving the application and proxying API requests.
- Node.js and Express for backend services.
- MongoDB for application data.
- JWT middleware for authentication and authorization.
The Tor container runs using the dedicated debian-tor user.
The Onion Service identity is designed to persist through a Docker volume, and telemetry is disabled by default.
These are useful foundations, but secure and reliable operation still requires runtime validation.
What we tested on an isolated VPS
We created a dedicated Docker test environment rather than deploying the pull request directly into production.
The environment used separate containers, an internal Docker network, and a dedicated MongoDB volume.
No application ports were published to the VPS host as part of these tests.
1. Backend and MongoDB
The backend image compiled successfully.
However, the original backend initially failed during startup because of an incorrect middleware import:
Cannot find module '../../../src/middleware/auth'
The affected file was:
backend/src/routes/metaverse.js
We tested a one-line local correction:
-const { authenticate, optionalAuthenticate } = require('../../../src/middleware/auth');
+const { authenticate, optionalAuthenticate } = require('../middleware/auth');
After rebuilding with that local change, the backend started and connected to MongoDB.
The health endpoint returned HTTP 200.
Important: this fix was tested locally; the pull request itself still needs the correction.
2. Ledger and Economics APIs
We also exercised the application's experimental API endpoints.
Results included:
| API | Result |
|---|---|
GET /api/ledger |
HTTP 200 |
GET /api/ledger/balances |
HTTP 200 |
POST /api/economics/simulate |
HTTP 200 |
The simulation returned internally consistent results using synthetic inputs, including a GMV of 200, revenue of 10, costs of 22, and a monthly margin of -12.
These tests demonstrate working API responses in the isolated environment, not production financial operations.
3. Authentication and authorization
We ran seven targeted checks across Chat, Notifications, and an administrative metrics endpoint.
All seven passed.
The tests included requests without tokens, invalid bearer tokens, valid synthetic user tokens, and valid synthetic administrator tokens.
The administrative endpoint returned:
No token -> HTTP 401
Standard user -> HTTP 403
Administrator -> HTTP 200
This gives us encouraging evidence about the specific middleware paths we tested, although it is not a complete security audit.
4. React and Nginx
The React production build completed successfully.
The frontend Docker image also built, and Nginx validated on the isolated Docker network.
We confirmed:
GET / -> 200
GET /synthetic-spa-route -> 200
GET /api/ledger -> 200
GET /api/ledger/balances -> 200
The last two requests passed through the Nginx reverse proxy to the backend.
We also identified a build-hardening improvement.
The frontend Dockerfile currently includes:
RUN npm run build || echo "Build skipped for now"
That can conceal future React compilation failures.
For a deployment image, we recommend making the build fail when compilation fails:
RUN npm run build
The most interesting challenge: Tor and Docker DNS
The Onion Service component introduced a particularly useful debugging exercise.
The original entrypoint generated a Tor configuration with a destination similar to:
HiddenServicePort 80 frontend:80
Here, frontend is the Docker Compose service name.
Docker containers can resolve that hostname through their internal DNS service.
However, when we tested this configuration with Tor 0.4.9.11, the validation failed:
Unparseable address in hidden service port configuration.
Failed to parse/validate config.
Using a numeric IP address passed Tor's configuration validation.
We therefore tested a local candidate approach: resolve the Docker service name before generating torrc.
For example:
TARGET_HOST=${ONION_TARGET_HOST:-frontend}
TARGET_IP=$(getent ahostsv4 "$TARGET_HOST" | awk 'NR == 1 {print $1}')
Then generate the destination using the resolved address:
HiddenServicePort 80 <resolved-container-ip>:80
In our isolated environment, Docker DNS resolved the frontend to an internal IP.
We confirmed that:
- The Tor container could resolve the frontend hostname.
- The frontend responded with HTTP 200 from that Docker network.
- The configuration using the resolved IP passed
tor --verify-config. - The modified Onion image built successfully.
- The modified entrypoint generated a configuration accepted by Tor.
But there is a catch
Docker container IP addresses are not necessarily stable.
If the frontend is recreated, its address may change.
Resolving the address only once at startup may therefore leave Tor pointing at an outdated destination.
This is why we're treating the change as a tested candidate fix, not a final production solution.
A robust design needs to account for startup readiness, service recreation, address changes, and safe reconfiguration.
Privacy and operational considerations
Supporting .onion access does not automatically make the entire application anonymous.
Authentication, application logs, external resources, browser behavior, and telemetry all matter.
Our current approach emphasizes separation of services, protection of Onion identity material, and explicit privacy boundaries.
Telemetry is disabled by default.
We also want to improve the Tor health check. Merely detecting a generated Onion hostname file does not prove that the service is reachable through the Tor network.
What is still missing?
Before we can describe the Onion deployment as ready for public use, we need to complete several tasks:
- Integrate the backend authentication import correction.
- Implement resilient destination resolution for Tor.
- Handle frontend recreation and Docker DNS readiness.
- Improve Tor health and connectivity checks.
- Make frontend Docker builds fail reliably on compilation errors.
- Validate the actual Onion Service in a controlled Tor-network test.
- Verify browser functionality and end-to-end behavior.
- Review application privacy boundaries and security before public deployment.
We have not yet launched or independently verified a publicly reachable MyZubster .onion service through these tests.
The work is in active development and review.
Why this matters
For us, open source means more than publishing code.
It means giving contributors meaningful work, recognizing their contributions, reproducing problems independently, and sharing evidence even when something doesn't work yet.
Nicola's N4K48 work and Wasim's ongoing contribution demonstrate how independent efforts can be reviewed and developed toward a shared technical goal.
And the Tor integration raises questions that other Docker and privacy-focused developers may have encountered before.
We would love feedback from the community
If you have experience with Tor Onion Services, Docker DNS, container lifecycle management, Nginx, or privacy-conscious infrastructure, we'd especially appreciate your input.
How would you handle a Tor Onion Service backend whose Docker container IP can change after recreation?
Would you use a local forwarding proxy, a service-discovery strategy, controlled Tor configuration reloads, or something else?
We're interested in practical, reproducible approaches.
Explore the project and contributors
MyZubster repository:
https://github.com/MyZubster-Ecosystem/myzubster
MyZubster website:
https://www.myzubster.com/
Wasim — GitHub profile:
https://github.com/wasim-builds
Wasim — current pull request #1582:
https://github.com/MyZubster-Ecosystem/myzubster/pull/1582
Nicola / N4K48 — original contribution #1457:
https://github.com/MyZubster-Ecosystem/myzubster/pull/1457
A sincere thank you to Wasim and Nicola for contributing their work to the MyZubster ecosystem.
We're building this step by step, testing carefully, and keeping the process open for people who want to help.
The next milestone isn't simply having an Onion address. It's making that address reliable, secure, maintainable, and useful.
And that's something worth building together.
Top comments (1)
tr.ee/dev-to