<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel Ioni</title>
    <description>The latest articles on DEV Community by Daniel Ioni (@danielioni).</description>
    <link>https://dev.to/danielioni</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4014925%2F997f1a4a-e9ae-47a4-a2fd-cb9c37f82a9f.jpg</url>
      <title>DEV Community: Daniel Ioni</title>
      <link>https://dev.to/danielioni</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/danielioni"/>
    <language>en</language>
    <item>
      <title>We Built Our First Isolated Tor v3 Onion Service for MyZubster 🧅</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Sat, 17 Oct 2026 07:21:20 +0000</pubDate>
      <link>https://dev.to/danielioni/we-built-our-first-isolated-tor-v3-onion-service-for-myzubster-1g87</link>
      <guid>https://dev.to/danielioni/we-built-our-first-isolated-tor-v3-onion-service-for-myzubster-1g87</guid>
      <description>&lt;h1&gt;
  
  
  We Built Our First Isolated Tor v3 Onion Service for MyZubster 🧅
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;An open-source experiment in privacy, Docker isolation, and decentralized infrastructure.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At MyZubster, we're exploring how independent contributors can build and operate interoperable services without relying on a single centralized infrastructure.&lt;/p&gt;

&lt;p&gt;Today, we reached a new technical milestone: &lt;strong&gt;our first isolated Tor v3 Onion pilot successfully completed the Tor bootstrap process and displayed its experimental web page.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a small but meaningful step toward our broader vision.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Built
&lt;/h2&gt;

&lt;p&gt;The pilot uses a deliberately minimal architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tor Browser
    |
    v
Tor v3 Onion Service
    |
    v
Isolated HAProxy
    |
    v
Static MyZubster Pilot Page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The environment includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A dedicated Tor container running Tor 0.4.9.11&lt;/li&gt;
&lt;li&gt;An isolated HAProxy container&lt;/li&gt;
&lt;li&gt;Separate Docker networks&lt;/li&gt;
&lt;li&gt;A persistent volume for the Onion service identity&lt;/li&gt;
&lt;li&gt;A static experimental page&lt;/li&gt;
&lt;li&gt;No intended access to MyZubster production APIs, wallets, or payment services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The proxy was configured to return HTTP 200 for the homepage and HTTP 404 for &lt;code&gt;/api&lt;/code&gt; and &lt;code&gt;/admin&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our First Successful Tor Bootstrap
&lt;/h2&gt;

&lt;p&gt;During the initial tests, we encountered Docker permission errors when Tor attempted to switch to its dedicated system user.&lt;/p&gt;

&lt;p&gt;After adjusting the container capabilities, Tor started successfully.&lt;/p&gt;

&lt;p&gt;The logs eventually confirmed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bootstrapped 100% (done): Done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also generated a dedicated Onion v3 address:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="http://lrkjukwcbn7ftl7nkhuaefarwqymxx6xzopt77qa7mc4uinbdxrzn4id.onion" rel="noopener noreferrer"&gt;http://lrkjukwcbn7ftl7nkhuaefarwqymxx6xzopt77qa7mc4uinbdxrzn4id.onion&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The experimental page displays:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;MyZubster Onion Pilot&lt;br&gt;&lt;br&gt;
Experimental Tor v3 service.&lt;br&gt;&lt;br&gt;
Isolated technical pilot without API or payment access.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This demonstrates an initial working proof of concept. It is not yet a production-ready, independently security-audited deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does This Matter?
&lt;/h2&gt;

&lt;p&gt;MyZubster is exploring a model where contributors can connect their own projects, services, skills, and infrastructure.&lt;/p&gt;

&lt;p&gt;Our longer-term goals include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Independent infrastructure nodes&lt;/li&gt;
&lt;li&gt;Interoperability between contributor projects&lt;/li&gt;
&lt;li&gt;Privacy-conscious access through Tor&lt;/li&gt;
&lt;li&gt;Verifiable contributions and digital contributor passports&lt;/li&gt;
&lt;li&gt;Community-driven governance and resource sharing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Decentralization is not simply adding a blockchain or an Onion address.&lt;/p&gt;

&lt;p&gt;It requires real operational independence, reproducible deployments, clear security boundaries, and contributors who can maintain services without depending on a single administrator.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next? Contributor-Led Development
&lt;/h2&gt;

&lt;p&gt;We're inviting &lt;strong&gt;@wasim-builds&lt;/strong&gt; to help advance the next phase of this Tor/HAProxy pilot.&lt;/p&gt;

&lt;p&gt;The proposed work includes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reproducible Docker Compose configuration&lt;/li&gt;
&lt;li&gt;Network isolation and threat-model documentation&lt;/li&gt;
&lt;li&gt;Meaningful health checks and rollback procedures&lt;/li&gt;
&lt;li&gt;End-to-end Tor Browser testing&lt;/li&gt;
&lt;li&gt;Recovery tests after container restarts or network changes&lt;/li&gt;
&lt;li&gt;Independent code review&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is an opt-in open-source contribution proposal, not a confirmed paid assignment.&lt;/p&gt;

&lt;p&gt;You can follow the technical discussion here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1584" rel="noopener noreferrer"&gt;MyZubster GitHub — Tor Onion Pilot #1584&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1605" rel="noopener noreferrer"&gt;Contributor Operations and DAO Pilots #1605&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Join the Experiment
&lt;/h2&gt;

&lt;p&gt;We're looking for developers interested in Tor, Docker, networking, privacy engineering, and interoperable open-source infrastructure.&lt;/p&gt;

&lt;p&gt;You don't need to operate a VPS or join a DAO to contribute.&lt;/p&gt;

&lt;p&gt;Start with a review, a reproducible test, documentation, or a small pull request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal isn't to claim we've already decentralized everything. It's to build and verify one independent component at a time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Explore the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/MyZubster-Ecosystem/myzubster" rel="noopener noreferrer"&gt;https://github.com/MyZubster-Ecosystem/myzubster&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Website: &lt;a href="https://www.myzubster.com/" rel="noopener noreferrer"&gt;https://www.myzubster.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;MyZubster — Connecting people, knowledge, projects, and independent infrastructure.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  opensource #tor #docker #privacy
&lt;/h1&gt;

</description>
      <category>opensource</category>
      <category>tor</category>
      <category>docker</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Building the MyZubster Metaverse: Secure Sessions, Verified Identities and the Road to Realtime</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Sat, 17 Oct 2026 05:19:27 +0000</pubDate>
      <link>https://dev.to/danielioni/building-the-myzubster-metaverse-secure-sessions-verified-identities-and-the-road-to-realtime-57i5</link>
      <guid>https://dev.to/danielioni/building-the-myzubster-metaverse-secure-sessions-verified-identities-and-the-road-to-realtime-57i5</guid>
      <description>&lt;h1&gt;
  
  
  Building the MyZubster Metaverse: Secure Sessions, Verified Identities and the Road to Realtime
&lt;/h1&gt;

&lt;p&gt;The MyZubster Metaverse is evolving from a visual experiment into a real, testable multiplayer environment.&lt;/p&gt;

&lt;p&gt;Our objective is not simply to render avatars on a page. We are building an open-source world where guest users, verified contributors and independent communities can interact through a shared identity and knowledge layer.&lt;/p&gt;

&lt;p&gt;The public entry point is now available here:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.myzubster.com/metaverse" rel="noopener noreferrer"&gt;https://www.myzubster.com/metaverse&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article explains what we have implemented, the security work completed in our latest development cycle, the current architecture and what still needs to be built before the platform can be considered production-ready at scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the MyZubster Metaverse is
&lt;/h2&gt;

&lt;p&gt;The MyZubster Metaverse is intended to connect several parts of the ecosystem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;public and verified user profiles;&lt;/li&gt;
&lt;li&gt;GitHub contributor identities;&lt;/li&gt;
&lt;li&gt;Zorgax characters;&lt;/li&gt;
&lt;li&gt;Verified Knowledge;&lt;/li&gt;
&lt;li&gt;community spaces;&lt;/li&gt;
&lt;li&gt;Development Requests;&lt;/li&gt;
&lt;li&gt;open-source contributions;&lt;/li&gt;
&lt;li&gt;optional rewards and bounties;&lt;/li&gt;
&lt;li&gt;future immersive WebXR experiences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current version focuses on the foundations: identity, presence, sessions and authenticated world actions.&lt;/p&gt;

&lt;p&gt;Users can currently enter the world as guests without requiring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a cryptocurrency wallet;&lt;/li&gt;
&lt;li&gt;identity documents;&lt;/li&gt;
&lt;li&gt;a GitHub account;&lt;/li&gt;
&lt;li&gt;an installed application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verified MyZubster explorers can also be displayed with their associated public GitHub identity.&lt;/p&gt;

&lt;p&gt;This gives us two different identity levels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Guest explorers&lt;/strong&gt;, who can enter quickly using a public display name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verified explorers&lt;/strong&gt;, whose public character is linked to an authorized account or GitHub identity.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Verification here does not mean KYC or legal identity verification. It only represents a confirmed connection with the declared public account.&lt;/p&gt;




&lt;h1&gt;
  
  
  What we have implemented
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Public Metaverse entry point
&lt;/h2&gt;

&lt;p&gt;The production interface now provides a dedicated Metaverse route:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
text
https://www.myzubster.com/metaverse
The entry interface includes:
- guest onboarding;
- public display-name selection;
- automatic character creation;
- verified explorer discovery;
- multilingual UI support;
- links to account-based authentication.
The guest path is intentionally lightweight. A user can explore the world without being forced to create a permanent account first.
2. World presence model
The backend maintains a presence record for each active Metaverse participant.
A presence can contain information such as:
{
  sessionId,
  displayName,
  characterId,
  accountId,
  githubLogin,
  position,
  status,
  lastSeenAt
}
The public representation deliberately excludes private credentials and internal security fields.
Presence data is used by the world API to support operations such as:
- joining the world;
- synchronizing visible participants;
- moving a character;
- sending chat messages;
- publishing emotes;
- leaving the world;
- retrieving world events.
Before our latest security work, knowing a public session identifier could potentially be sufficient to target mutable session routes. That is not an acceptable trust boundary for a multiplayer environment.
A public identifier must identify a session, but it must never prove ownership of it.
The latest milestone: possession-bound sessions
Our latest merged pull request introduces possession-based session credentials.
Pull request:
👉 MyZubster PR #34 — Bind world actions to session credentials
Production commit:
148a798
This work creates a security boundary between:
- the public session identifier;
- the private credential required to control that session.
How session creation now works
When a player joins the world, the backend creates:
1. a public sessionId;
2. a cryptographically random private session token.
The token is generated from 32 random bytes, providing 256 bits of entropy.
Conceptually:
const sessionToken = crypto.randomBytes(32).toString("base64url");
The raw token is returned to the client only when the session is created.
It is never included in public player data.
3. Only the token digest is stored
The backend does not persist the raw session token.
Instead, it calculates a SHA-256 digest:
const tokenHash = crypto
  .createHash("sha256")
  .update(sessionToken)
  .digest("hex");
The presence record stores the digest:
{
  sessionId,
  sessionTokenHash: tokenHash
}
This reduces the impact of an accidental database disclosure. An attacker reading the presence collection would not immediately obtain the active bearer credentials.
A token cannot be recovered directly from its digest.
4. Constant-time credential verification
Mutable world routes verify the submitted credential against the stored digest.
The comparison uses a constant-time operation to reduce timing side-channel exposure:
crypto.timingSafeEqual(expectedBuffer, receivedBuffer);
Credential validation is centralized in an ownership middleware similar to:
requireOwnedSession
The middleware checks that:
- the session exists;
- the private token was provided;
- the submitted token produces the expected digest;
- the credential belongs to the requested session.
Missing or invalid credentials result in an unauthorized response.
HTTP 401 Unauthorized
5. Protected world actions
The following routes are now protected by session ownership verification:
/sync
/move
/chat
/emote
/leave
/events
Knowing another player’s public sessionId is no longer sufficient to:
- move that player;
- send messages as that player;
- publish their emotes;
- synchronize private session state;
- remove them from the world;
- retrieve protected session events.
This closes an important class of horizontal session-ownership vulnerabilities.
6. Tokens are sent through a dedicated header
The frontend now sends the private credential through:
X-Metaverse-Session-Token: &amp;lt;private-token&amp;gt;
Example:
const createSessionHeaders = (sessionToken) =&amp;gt; ({
  "X-Metaverse-Session-Token": sessionToken,
});
The token is not placed inside normal public JSON payloads:
{
  "sessionId": "public-session-id",
  "x": 120,
  "y": 80
}
Separating authorization data from world-state payloads makes the security boundary clearer and reduces the risk of credentials being copied into:
- application logs;
- analytics events;
- public state;
- debugging output;
- serialized world events.
The backend temporarily supports a legacy request-body token for compatibility, but the header is the canonical client mechanism.
7. Credentials remain in memory
The browser keeps the Metaverse session credential in React runtime memory.
We deliberately avoid storing it in:
localStorage
sessionStorage
public profile objects
world event payloads
In-memory credentials disappear when the page or browser session is closed.
This does not eliminate every browser-side threat, especially an active cross-site scripting vulnerability, but it reduces long-lived credential exposure.
Privacy and identity boundaries
The Metaverse must distinguish several concepts that are often incorrectly combined:
Account identity
      ↓
Public profile
      ↓
Character identity
      ↓
Temporary world session
      ↓
Private session credential
A public GitHub profile proves control of a public contributor account only when an authorization flow verifies it.
It does not automatically prove:
- legal identity;
- employment;
- institutional affiliation;
- ownership of an organization;
- eligibility for payment;
- control of a blockchain wallet.
Similarly, the public session ID identifies an active world session, but only the private session credential authorizes mutations for that session.
These distinctions are now documented in the repository’s Metaverse privacy and security documentation.
Testing completed
The session-ownership work includes backend, frontend and static architecture tests.
Backend verification
The targeted backend suites cover:
- secure token creation;
- SHA-256 digest persistence;
- successful authorized operations;
- missing credential rejection;
- invalid credential rejection;
- attempts to mutate another player’s session;
- exclusion of credentials from public payloads;
- protected movement, chat, emote, synchronization and leave routes.
Result:
3 targeted backend suites
22 tests passed
Frontend verification
Frontend tests validate:
- token retention in application memory;
- use of X-Metaverse-Session-Token;
- authorized API requests;
- session-aware world operations;
- absence of the token from public request bodies.
Result:
1 targeted frontend suite
5 tests passed
Build and integration verification
We also completed:
Production frontend build: successful
git diff --check: clean
MYZ-164 Marketplace regression: passed
Vercel preview deployments: successful
Main deployment: successful
The implementation was squash-merged into main and deployed through the production workflow.
Current architecture
The current architecture can be summarized as:
React Metaverse Client
        |
        | HTTPS requests
        | X-Metaverse-Session-Token
        v
Node.js / Express Metaverse API
        |
        | ownership middleware
        | token hashing and verification
        v
Metaverse Presence Storage
        |
        v
Shared world polling and event synchronization
The current system provides a practical foundation, but it is not yet the final realtime architecture.
What still needs to be completed
1. Realtime transport
The current shared-world model still relies on polling-based synchronization in important areas.
The next architectural step is to introduce a proper realtime transport, most likely based on:
- WebSockets;
- Socket.IO;
- Server-Sent Events for selected read-only streams;
- a message broker when horizontal scaling becomes necessary.
Realtime transport must include:
- authenticated connection establishment;
- session-token validation;
- connection-to-session binding;
- reconnect handling;
- event ordering;
- duplicate-event protection;
- backpressure controls;
- presence expiry.
We should not simply move the existing endpoints to WebSockets. The event protocol must be explicitly designed and versioned.
2. Distributed rate limiting
In-memory rate limiting is insufficient when multiple backend instances process requests.
We still need a distributed limiter for:
- session creation;
- movement events;
- chat messages;
- emotes;
- authentication attempts;
- reconnect operations.
Possible storage layers include Redis or another shared atomic counter service.
The limiter should support dimensions such as:
IP address
account ID
session ID
route
world or room ID
time window
Movement events should have different limits from chat or authentication attempts.
3. Session lifecycle and revocation
Possession-bound credentials are now implemented, but lifecycle management needs additional work.
Remaining tasks include:
- explicit session expiration;
- server-side revocation;
- automatic removal of abandoned sessions;
- token rotation;
- reconnect-safe credential renewal;
- maximum active sessions per account;
- logout across devices;
- suspicious-session invalidation.
We also need clear rules for temporary guest sessions versus authenticated persistent characters.
4. Stronger account authentication
Verified characters need a canonical authentication path.
The roadmap includes:
- stable GitHub OAuth integration;
- verified account-to-character linking;
- protection against account-link substitution;
- clear reauthorization flows;
- audit trails for identity changes;
- recovery procedures.
The Metaverse must never trust a GitHub username supplied directly by the browser without verifying control of that account.
5. Privacy-safe analytics
We need operational visibility without building invasive tracking.
Metrics should focus on system health:
- active anonymous sessions;
- session duration ranges;
- API latency;
- reconnect frequency;
- rate-limit events;
- server errors;
- world capacity;
- event throughput.
We should avoid collecting unnecessary personal identifiers.
Analytics data needs:
- documented retention;
- aggregation;
- access restrictions;
- deletion rules;
- explicit separation from public profile data.
6. Reliability and load testing
The next reliability milestone must test realistic concurrent behavior.
Scenarios should include:
- many simultaneous joins;
- movement bursts;
- reconnect storms;
- inactive sessions;
- malformed events;
- duplicate events;
- slow clients;
- backend restarts;
- database latency;
- multiple application instances.
The system needs measurable targets for:
maximum concurrent participants
p95 and p99 API latency
event delivery delay
error rate
recovery time
presence consistency
7. Complete end-to-end acceptance tests
Unit and integration tests are not sufficient for the entire world flow.
We still need automated E2E tests covering:
Guest joins
→ session credential is issued
→ character becomes visible
→ movement is synchronized
→ chat and emotes are authorized
→ another session cannot impersonate the guest
→ reconnect behavior is correct
→ session leaves or expires
Equivalent tests are required for verified characters.
8. WebXR and immersive clients
WebXR remains a later-stage objective.
Before adding immersive rendering, we need to stabilize:
- authentication;
- world protocol;
- realtime synchronization;
- abuse prevention;
- accessibility;
- performance;
- mobile behavior.
The WebXR client should consume the same documented world protocol rather than implementing an independent backend.
This will allow multiple compatible clients:
2D web client
3D browser client
WebXR client
mobile client
community-built client
9. Dependency security debt
The current repository still reports dependency vulnerabilities during npm audit.
The latest Metaverse pull request did not modify package or lock files, so these findings were not introduced by the session-security work.
However, they remain real technical debt.
They must be handled in a separate dependency-hardening cycle involving:
1. vulnerability classification;
2. identification of production versus development exposure;
3. safe direct dependency updates;
4. transitive dependency analysis;
5. regression testing;
6. replacement of abandoned packages;
7. lockfile regeneration;
8. SBOM and audit evidence updates.
We should not hide or disable the audit simply to obtain a green badge.
Proposed next development sequence
The recommended order is:
1. Stabilize session expiration and revocation
2. Implement distributed rate limiting
3. Complete canonical account authentication
4. Design the versioned realtime event protocol
5. Introduce authenticated WebSocket connections
6. Add privacy-safe operational metrics
7. Execute reliability and load testing
8. Complete full guest and verified-user E2E tests
9. Resolve dependency security debt
10. Start the WebXR client milestone
Security and reliability must come before graphical complexity.
Open-source collaboration
We are looking for developers and independent testers interested in:
- Node.js and Express;
- React;
- realtime systems;
- WebSockets;
- MongoDB;
- Redis;
- application security;
- abuse prevention;
- privacy engineering;
- load testing;
- WebXR;
- open-source documentation.
Repository:
👉 https://github.com/danieldirimini-myzubster/myzubster
Public Metaverse:
👉 https://www.myzubster.com/metaverse
Recent security implementation:
👉 https://github.com/danieldirimini-myzubster/myzubster/pull/34
Conclusion
The MyZubster Metaverse now has a stronger technical foundation.
We have moved from trusting public session identifiers to enforcing private possession-bound credentials for mutable world actions.
The platform now includes:
- public guest access;
- verified explorer discovery;
- character presence;
- protected world operations;
- 256-bit session credentials;
- SHA-256 token digests;
- constant-time verification;
- header-based authorization;
- memory-only client credentials;
- backend and frontend tests;
- automated production deployment.
The next challenge is no longer simply creating more visual features.
It is building a secure, realtime, distributed and independently testable world protocol that can support different clients and communities without sacrificing privacy or transparency.
That is the direction of the next MyZubster Metaverse milestones.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>metaverse</category>
      <category>security</category>
      <category>opensource</category>
    </item>
    <item>
      <title>MyZubster: From a Centralized VPS to a Network of Independent Nodes — The Work of Nicola N4K48 and Wasim</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Thu, 15 Oct 2026 05:24:59 +0000</pubDate>
      <link>https://dev.to/danielioni/myzubster-from-a-centralized-vps-to-a-network-of-independent-nodes-the-work-of-nicola-n4k48-and-7ci</link>
      <guid>https://dev.to/danielioni/myzubster-from-a-centralized-vps-to-a-network-of-independent-nodes-the-work-of-nicola-n4k48-and-7ci</guid>
      <description>&lt;h1&gt;
  
  
  MyZubster: From a Centralized VPS to a Network of Independent Nodes — The Work of Nicola N4K48 and Wasim
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Open source, Docker, Tor, interoperability, verifiable knowledge, and distributed governance: we are exploring an ecosystem where contributors can build, verify, and eventually operate independent nodes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decentralization does not begin with a cryptocurrency. It begins when multiple people can develop, verify, and operate a system without depending entirely on a single administrator.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the direction we are exploring with &lt;a href="https://myzubster.com" rel="noopener noreferrer"&gt;MyZubster&lt;/a&gt;, an open-source ecosystem connecting a marketplace, AI tools, independent projects, contributors, and verifiable knowledge.&lt;/p&gt;

&lt;p&gt;Our goal is not simply to create another marketplace.&lt;/p&gt;

&lt;p&gt;We want to build an ecosystem where developers, artists, researchers, and local communities can contribute their own projects, document their work, demonstrate their skills, and progressively operate independent infrastructure.&lt;/p&gt;

&lt;p&gt;Two contributors are helping turn this vision into a concrete technical roadmap:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nicola (N4K48) and Wasim (@wasim-builds).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Their work represents two complementary approaches to decentralization.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Nicola N4K48: Building an Independent Node
&lt;/h2&gt;

&lt;p&gt;Nicola is developing an experimental model connecting creative projects, digital catalogs, software agents, and Docker-based infrastructure.&lt;/p&gt;

&lt;p&gt;His work addresses a fundamental question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can an independent project maintain its technical autonomy while interoperating with the wider MyZubster ecosystem?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The documented checkpoints include local catalog and agent verification, alongside a previously completed authenticated connection test between the MyZubster VPS and the N4K48 node.&lt;/p&gt;

&lt;p&gt;Following credential rotation, the agent has remained offline pending authorization for any further authenticated testing.&lt;/p&gt;

&lt;p&gt;This is an important principle: decentralization must not come at the expense of security.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nicola's GitHub contributions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1583" rel="noopener noreferrer"&gt;DAO Pilot #1583 — Independent Node Replication and Interoperability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1505" rel="noopener noreferrer"&gt;Pilot Node Network #1505 — Connecting Independent Contributors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1520" rel="noopener noreferrer"&gt;Contributor Bridge #1520 — Opt-in Links and Interoperability Tests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1551" rel="noopener noreferrer"&gt;Second Independent Node Checkpoint #1551&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1499" rel="noopener noreferrer"&gt;Docker Node Reproduction #1499 — Proposed Bounty&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1176" rel="noopener noreferrer"&gt;Nicola Comics #1176 — Zorgax Catalog Integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1474" rel="noopener noreferrer"&gt;Contributor Update #1474 — Docker Nodes and NFT Provenance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nicola's work demonstrates how an independent creative project can also become an experiment in autonomous infrastructure, verifiable contributions, and interoperability.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Wasim: Designing Resilient and Reproducible Infrastructure
&lt;/h2&gt;

&lt;p&gt;While Nicola is experimenting with an independent node, Wasim is working on the technical foundations that could make this model safer, more resilient, and easier to reproduce.&lt;/p&gt;

&lt;p&gt;His contributions currently cover four major areas.&lt;/p&gt;

&lt;h3&gt;
  
  
  A. Tor Onion, HAProxy, and Docker
&lt;/h3&gt;

&lt;p&gt;Wasim has prepared an architecture proposal involving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker containerization&lt;/li&gt;
&lt;li&gt;HAProxy reverse proxy&lt;/li&gt;
&lt;li&gt;Tor Onion connectivity&lt;/li&gt;
&lt;li&gt;Threat modeling&lt;/li&gt;
&lt;li&gt;Reproducible local tests&lt;/li&gt;
&lt;li&gt;Rollback procedures&lt;/li&gt;
&lt;li&gt;Security evidence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The objective is to investigate ways of improving service resilience and reducing infrastructure dependencies.&lt;/p&gt;

&lt;p&gt;His reported tests were performed in an isolated local environment, without exposing host ports, using real credentials, or consuming external VPS resources.&lt;/p&gt;

&lt;p&gt;The design is documented in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1584" rel="noopener noreferrer"&gt;Issue #1584 — Wasim's DAO Pilot: Tor Onion and Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1610" rel="noopener noreferrer"&gt;Draft PR #1610 — Tor/HAProxy/Docker Architecture&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; This is currently a technical proposal under review, not a production Tor service or an operational independent node.&lt;/p&gt;

&lt;h3&gt;
  
  
  B. Pastebin → GitHub → Zorgax Interoperability
&lt;/h3&gt;

&lt;p&gt;Another contribution focuses on connecting external information sources to MyZubster's verifiable knowledge system.&lt;/p&gt;

&lt;p&gt;Wasim has updated a technical design for an evidence-ingestion workflow with security protections, provenance checks, and human review.&lt;/p&gt;

&lt;p&gt;The proposal includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTPS-only source restrictions&lt;/li&gt;
&lt;li&gt;Redirect blocking&lt;/li&gt;
&lt;li&gt;Protection against requests to private network addresses&lt;/li&gt;
&lt;li&gt;Deterministic rejection tests&lt;/li&gt;
&lt;li&gt;Payload limits&lt;/li&gt;
&lt;li&gt;Sensitive-information filtering&lt;/li&gt;
&lt;li&gt;Mandatory human review before accepting evidence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The proposed evidence format uses:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;myzubster.knowledge.evidence.v1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The goal is to establish a clear distinction between information that has been submitted, imported, reviewed, and verified.&lt;/p&gt;

&lt;p&gt;External information should not automatically become trusted knowledge.&lt;/p&gt;

&lt;p&gt;Related work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1591" rel="noopener noreferrer"&gt;Issue #1591 — Pastebin → GitHub → Zorgax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1611" rel="noopener noreferrer"&gt;Draft PR #1611 — Interoperability Design&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  C. Contributor Passport Privacy
&lt;/h3&gt;

&lt;p&gt;Wasim has also participated in reviewing privacy protections for the MyZubster Contributor Passport.&lt;/p&gt;

&lt;p&gt;The underlying principle is straightforward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A contributor's personal information should not become publicly accessible without appropriate consent controls.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is particularly important when connecting identities, technical contributions, skills, evidence, and reputation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1599" rel="noopener noreferrer"&gt;PR #1599 — Contributor Passport Privacy Gate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  D. GitHub CI Reliability and Hardhat 3
&lt;/h3&gt;

&lt;p&gt;Decentralized infrastructure also requires reliable and independently verifiable software.&lt;/p&gt;

&lt;p&gt;Wasim has contributed to GitHub Actions troubleshooting and reviewed the migration to Hardhat 3.&lt;/p&gt;

&lt;p&gt;His review covered a change separating NFT tooling from the main application's dependency structure.&lt;/p&gt;

&lt;p&gt;We are also investigating CI failures in our development fork.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1608" rel="noopener noreferrer"&gt;Issue #1608 — CI Reliability and Security Audit Triage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1604" rel="noopener noreferrer"&gt;PR #1604 — Hardhat 3 Migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/danieldirimini-myzubster/myzubster/pull/32" rel="noopener noreferrer"&gt;PR #32 — Fork Security Audit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. What Are We Building Together?
&lt;/h2&gt;

&lt;p&gt;Our long-term objective is to evolve MyZubster into a federated ecosystem of independently operated projects.&lt;/p&gt;

&lt;p&gt;A simplified conceptual architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                MyZubster Ecosystem
                        |
          +-------------+-------------+
          |             |             |
      Nicola Node   Future Node   Future Node
        N4K48        Wasim?       Contributor
          |             |             |
     Docker/Agent   Tor/HAProxy    Independent
     Local Catalog  Docker         Application
          |             |             |
          +-------------+-------------+
                        |
               Interoperability
                        |
                  Zorgax / APIs
                        |
              Knowledge &amp;amp; Evidence
                        |
              Contributor Passport
                        |
                   Marketplace
                        |
                DAO Governance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;This diagram represents a proposed architecture, not an already operational federation. Wasim has not yet confirmed an independently operated production node.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The idea is that contributors could retain control over their own projects while choosing how to connect them to a shared ecosystem.&lt;/p&gt;

&lt;p&gt;Different MyZubster components would serve complementary purposes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zorgax&lt;/strong&gt; could help contributors explore and organize connected knowledge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Contributor Passport&lt;/strong&gt; could document identities, skills, contributions, and supporting evidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Marketplace&lt;/strong&gt; could enable contributors to offer services, products, and technical expertise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Independent nodes&lt;/strong&gt; could host projects without requiring every service to run on the founder's central VPS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DAO governance&lt;/strong&gt; could eventually help distribute responsibilities, decision-making, infrastructure costs, and rewards.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Why We Want to Reduce Dependence on a Single VPS
&lt;/h2&gt;

&lt;p&gt;A centralized infrastructure creates practical limitations.&lt;/p&gt;

&lt;p&gt;One administrator may be responsible for hosting costs, maintenance, security, availability, and operational decisions.&lt;/p&gt;

&lt;p&gt;This creates both technical and organizational risks.&lt;/p&gt;

&lt;p&gt;Our goal is to investigate a model in which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contributors can independently operate parts of their infrastructure.&lt;/li&gt;
&lt;li&gt;Projects communicate through documented interfaces.&lt;/li&gt;
&lt;li&gt;Credentials remain under the control of their respective operators.&lt;/li&gt;
&lt;li&gt;Tests can be reproduced without production access.&lt;/li&gt;
&lt;li&gt;Infrastructure costs and responsibilities can be distributed.&lt;/li&gt;
&lt;li&gt;Contributions and knowledge are supported by verifiable evidence.&lt;/li&gt;
&lt;li&gt;Independent projects retain their identity and autonomy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This transition cannot be achieved through a single deployment.&lt;/p&gt;

&lt;p&gt;It requires reproducible tests, shared protocols, security reviews, operational agreements, and genuine collaboration.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Toward a DAO and Sustainable Contributor Economy
&lt;/h2&gt;

&lt;p&gt;We are also exploring a DAO-inspired governance model for distributing work, responsibilities, costs, and rewards.&lt;/p&gt;

&lt;p&gt;Our goal is to avoid building an ecosystem whose long-term survival depends entirely on one founder's personal resources.&lt;/p&gt;

&lt;p&gt;However, a DAO is not created simply by issuing tokens or opening a GitHub discussion.&lt;/p&gt;

&lt;p&gt;A sustainable governance model requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clearly defined responsibilities&lt;/li&gt;
&lt;li&gt;Transparent decision-making&lt;/li&gt;
&lt;li&gt;Verifiable contributions&lt;/li&gt;
&lt;li&gt;Fair and documented compensation arrangements&lt;/li&gt;
&lt;li&gt;Sustainable infrastructure funding&lt;/li&gt;
&lt;li&gt;Dispute-resolution procedures&lt;/li&gt;
&lt;li&gt;Independent oversight&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These mechanisms remain under development.&lt;/p&gt;

&lt;p&gt;Related discussions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1554" rel="noopener noreferrer"&gt;Issue #1554 — DAO Roles, MYZ Rewards, and Treasury&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1560" rel="noopener noreferrer"&gt;Issue #1560 — Removing the Founder/VPS Single Point of Failure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1580" rel="noopener noreferrer"&gt;Issue #1580 — Independent Node Operator Program&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1605" rel="noopener noreferrer"&gt;Issue #1605 — Contributor Operations v1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our objective is to develop a model in which independent contributors can create value, receive recognition, and potentially participate in sustainable economic activity.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. What Has Been Tested, and What Still Needs to Be Proven?
&lt;/h2&gt;

&lt;p&gt;One of the most important principles behind MyZubster is distinguishing proposals from verified implementations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Already documented
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GitHub contributions and technical reviews&lt;/li&gt;
&lt;li&gt;Local Docker testing&lt;/li&gt;
&lt;li&gt;N4K48 catalog and agent checks&lt;/li&gt;
&lt;li&gt;A previously completed authenticated VPS-to-node checkpoint&lt;/li&gt;
&lt;li&gt;Hardhat 3 migration review&lt;/li&gt;
&lt;li&gt;Contributor Passport privacy review&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Currently under review
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Wasim's Tor/HAProxy architecture&lt;/li&gt;
&lt;li&gt;Pastebin-to-Zorgax evidence ingestion&lt;/li&gt;
&lt;li&gt;Additional CI reliability work&lt;/li&gt;
&lt;li&gt;Reproducibility and security of the proposed infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Not yet demonstrated
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A stable federation of multiple independently operated production nodes&lt;/li&gt;
&lt;li&gt;Effective failover between independent infrastructure operators&lt;/li&gt;
&lt;li&gt;Fully operational decentralized governance&lt;/li&gt;
&lt;li&gt;Sustainable distributed infrastructure financing&lt;/li&gt;
&lt;li&gt;End-to-end interoperability across all proposed services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These distinctions matter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We want our technical claims to be supported by reproducible evidence, not just ambitious announcements.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  7. An Invitation to Open-Source Contributors
&lt;/h2&gt;

&lt;p&gt;MyZubster is open to developers, researchers, creators, and independent project operators interested in experimenting with interoperability and decentralization.&lt;/p&gt;

&lt;p&gt;You do not need to own a VPS or invest money to begin contributing.&lt;/p&gt;

&lt;p&gt;Useful contributions can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code reviews and security analysis&lt;/li&gt;
&lt;li&gt;Docker-based local experiments&lt;/li&gt;
&lt;li&gt;API interoperability testing&lt;/li&gt;
&lt;li&gt;Privacy and consent mechanisms&lt;/li&gt;
&lt;li&gt;Knowledge graph research&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Accessibility improvements&lt;/li&gt;
&lt;li&gt;Independent verification of technical evidence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We believe meaningful collaboration starts with small, clearly scoped, reproducible contributions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Explore the project
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Main project&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://myzubster.com" rel="noopener noreferrer"&gt;MyZubster Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster" rel="noopener noreferrer"&gt;MyZubster GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Nicola N4K48&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1583" rel="noopener noreferrer"&gt;Independent Node Pilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1520" rel="noopener noreferrer"&gt;Contributor Bridge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1505" rel="noopener noreferrer"&gt;Pilot Node Network&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Wasim&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1610" rel="noopener noreferrer"&gt;Tor/HAProxy/Docker Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1611" rel="noopener noreferrer"&gt;Pastebin/GitHub/Zorgax Interoperability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1608" rel="noopener noreferrer"&gt;CI and Security Audit Work&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Decentralization roadmap&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1560" rel="noopener noreferrer"&gt;Removing Centralized Infrastructure Dependencies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1554" rel="noopener noreferrer"&gt;DAO Governance and Sustainable Treasury&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion: Decentralization Is Something We Build Together
&lt;/h2&gt;

&lt;p&gt;MyZubster is not yet a fully decentralized federation.&lt;/p&gt;

&lt;p&gt;We are building and testing the components that could make such a federation possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nicola is helping us understand how an independent project can operate as a node.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wasim is helping us investigate how those nodes can become more secure, reproducible, interoperable, and resilient.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The next milestone is to verify that these components can work together safely, under distributed operational responsibility.&lt;/p&gt;

&lt;p&gt;We are not simply trying to connect servers.&lt;/p&gt;

&lt;p&gt;We are exploring how to connect &lt;strong&gt;people, projects, skills, evidence, and independent infrastructure&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And we want that process to remain open, transparent, and verifiable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The future of MyZubster should not depend on one person operating everything. It should be something contributors can build, verify, and sustain together.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Daniel Ioni (h4x0r)&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Creator &amp;amp; Administrator — MyZubster Ecosystem&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  opensource #decentralization #docker #webdev
&lt;/h1&gt;

</description>
      <category>myzubster</category>
      <category>docker</category>
      <category>wasim</category>
      <category>n4k48</category>
    </item>
    <item>
      <title>Building the MyZubster Metaverse: What We've Shipped and What's Next</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Thu, 15 Oct 2026 04:48:24 +0000</pubDate>
      <link>https://dev.to/danielioni/building-the-myzubster-metaverse-what-weve-shipped-and-whats-next-57mk</link>
      <guid>https://dev.to/danielioni/building-the-myzubster-metaverse-what-weve-shipped-and-whats-next-57mk</guid>
      <description>&lt;h1&gt;
  
  
  Building the MyZubster Metaverse: What We've Shipped and What's Next
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;From account-linked experiences to distributed abuse protection — an open-source development update.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Over the past development cycle, we've made meaningful progress on the MyZubster Metaverse.&lt;/p&gt;

&lt;p&gt;Our goal is not simply to build another virtual environment. We're exploring how a metaverse can connect people, independent projects, digital identities, knowledge, and real-world communities through an open-source ecosystem.&lt;/p&gt;

&lt;p&gt;Here's what we've completed, what we've tested, and what still needs work.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ What we've completed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Account-linked Metaverse experience
&lt;/h3&gt;

&lt;p&gt;The public Metaverse route is available at:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.myzubster.com/metaverse" rel="noopener noreferrer"&gt;https://www.myzubster.com/metaverse&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have implemented and tested the account-linked flow, connecting the Metaverse experience with MyZubster's broader authentication and user ecosystem.&lt;/p&gt;

&lt;p&gt;This provides a foundation for future interactions between digital identity, community participation, and independent projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Distributed rate limiting
&lt;/h3&gt;

&lt;p&gt;One of our latest milestones is the implementation of MongoDB-backed abuse protection.&lt;/p&gt;

&lt;p&gt;The system introduces rate limits for four Metaverse operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;join&lt;/code&gt; — entering a session&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sync&lt;/code&gt; — synchronizing session state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chat&lt;/code&gt; — sending messages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;emote&lt;/code&gt; — submitting interactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using a shared MongoDB-backed limiter helps coordinate abuse controls across application instances instead of relying exclusively on individual server memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Privacy-conscious throttling
&lt;/h3&gt;

&lt;p&gt;We've introduced HMAC-derived identifiers for throttling, rather than storing raw identifiers directly as rate-limit keys.&lt;/p&gt;

&lt;p&gt;The implementation also includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic TTL-based expiration&lt;/li&gt;
&lt;li&gt;HTTP &lt;code&gt;429 Too Many Requests&lt;/code&gt; responses&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Retry-After&lt;/code&gt; information&lt;/li&gt;
&lt;li&gt;Protection against spoofed proxy headers&lt;/li&gt;
&lt;li&gt;Documented privacy and retention boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These measures are intended to reduce abuse while limiting unnecessary retention of identifying information.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Fail-closed production behavior
&lt;/h3&gt;

&lt;p&gt;During review, we identified an important issue: if MongoDB became unavailable, the limiter could fall back to local in-memory behavior.&lt;/p&gt;

&lt;p&gt;We corrected this behavior so that the production configuration fails closed when the shared rate-limiting dependency is unavailable.&lt;/p&gt;

&lt;p&gt;This is an important security trade-off: availability may be reduced during an infrastructure failure, but abuse protection is not silently weakened.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Automated testing and GitHub integration
&lt;/h3&gt;

&lt;p&gt;The final development work included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;21 backend test suites passing&lt;/li&gt;
&lt;li&gt;125 backend tests passing in the final local verification&lt;/li&gt;
&lt;li&gt;Clean Git diff checks&lt;/li&gt;
&lt;li&gt;GitHub pull request review and merge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The distributed abuse protection was merged through:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PR #30 — &lt;code&gt;feat(metaverse): add distributed abuse rate limits&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/danieldirimini-myzubster/myzubster/pull/30" rel="noopener noreferrer"&gt;https://github.com/danieldirimini-myzubster/myzubster/pull/30&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Merge commit: &lt;code&gt;d911cadd824a1ca00b50cfead7929eb8f80934d0&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This represents completed implementation work, not a claim that every production-readiness requirement has been satisfied.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠️ What still needs to be completed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Dependency security remediation
&lt;/h3&gt;

&lt;p&gt;Our security pipeline continues to flag an existing dependency backlog.&lt;/p&gt;

&lt;p&gt;The latest reported audit included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;61 vulnerabilities in total&lt;/li&gt;
&lt;li&gt;43 high-severity vulnerabilities&lt;/li&gt;
&lt;li&gt;1 critical vulnerability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some remediation paths involve potentially breaking dependency upgrades, including a major Hardhat migration.&lt;/p&gt;

&lt;p&gt;We don't want to hide these findings or blindly apply incompatible updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next step:&lt;/strong&gt; review production versus development dependencies, assess actual exposure, submit targeted fixes, and rerun the security pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Final production verification
&lt;/h3&gt;

&lt;p&gt;The pull request has been merged, but the final production verification was interrupted before completion.&lt;/p&gt;

&lt;p&gt;We still need to verify the deployed commit, production health, and real behavior of the new rate limits.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Multi-user and load testing
&lt;/h3&gt;

&lt;p&gt;Passing unit and integration tests is not the same as demonstrating scalability.&lt;/p&gt;

&lt;p&gt;We want to validate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Concurrent user sessions&lt;/li&gt;
&lt;li&gt;Distributed synchronization&lt;/li&gt;
&lt;li&gt;Chat and interaction throttling&lt;/li&gt;
&lt;li&gt;MongoDB availability and recovery behavior&lt;/li&gt;
&lt;li&gt;Performance under realistic traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Contributor-led testing
&lt;/h3&gt;

&lt;p&gt;We're inviting open-source contributors to test the Metaverse independently, report reproducible bugs, review security assumptions, and improve documentation.&lt;/p&gt;

&lt;p&gt;A useful contribution doesn't have to be a large feature.&lt;/p&gt;

&lt;p&gt;A clear bug report, a failing test, or a small targeted pull request can move the project forward.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Independent infrastructure and decentralization
&lt;/h3&gt;

&lt;p&gt;MyZubster is also exploring interoperability with contributor-owned infrastructure.&lt;/p&gt;

&lt;p&gt;Our N4K48 pilot is an early step toward connecting independently managed projects and nodes.&lt;/p&gt;

&lt;p&gt;However, an independently hosted VPS is not automatically a decentralized network.&lt;/p&gt;

&lt;p&gt;We still need documented interfaces, tested recovery procedures, access controls, and independently reproducible interoperability tests.&lt;/p&gt;

&lt;p&gt;This work remains in progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  🌍 The bigger vision
&lt;/h2&gt;

&lt;p&gt;We want MyZubster to become a place where people can connect their real-world activities, creative work, services, and technical contributions to a shared digital ecosystem.&lt;/p&gt;

&lt;p&gt;The Metaverse is one component of that vision, alongside Zorgax, the Marketplace, and the Contributor Passport.&lt;/p&gt;

&lt;p&gt;But our immediate priority is simpler:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make the existing features reliable, secure, understandable, and useful before expanding their scope.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🤝 Join the development
&lt;/h2&gt;

&lt;p&gt;We're looking for contributors interested in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security and dependency remediation&lt;/li&gt;
&lt;li&gt;Backend and MongoDB testing&lt;/li&gt;
&lt;li&gt;Metaverse frontend testing&lt;/li&gt;
&lt;li&gt;Accessibility and user experience&lt;/li&gt;
&lt;li&gt;Docker and infrastructure automation&lt;/li&gt;
&lt;li&gt;Documentation and reproducible test cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster" rel="noopener noreferrer"&gt;https://github.com/MyZubster-Ecosystem/myzubster&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Contributor coordination:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1609" rel="noopener noreferrer"&gt;https://github.com/MyZubster-Ecosystem/myzubster/issues/1609&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Metaverse:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.myzubster.com/metaverse" rel="noopener noreferrer"&gt;https://www.myzubster.com/metaverse&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're interested, explore the repository, choose a focused task, and join the discussion.&lt;/p&gt;

&lt;p&gt;We're building MyZubster in the open — one verified contribution at a time.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Daniel Ioni (h4x0r)&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;MyZubster — Open-Source Ecosystem&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  opensource #webdev #nodejs #metaverse
&lt;/h1&gt;

</description>
      <category>metaverse</category>
      <category>opensource</category>
      <category>node</category>
      <category>myzubster</category>
    </item>
    <item>
      <title>Building an Evidence-First Contributor Network: How MyZubster Connects Independent Projects</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Tue, 13 Oct 2026 05:05:00 +0000</pubDate>
      <link>https://dev.to/danielioni/building-an-evidence-first-contributor-network-how-myzubster-connects-independent-projects-1pmh</link>
      <guid>https://dev.to/danielioni/building-an-evidence-first-contributor-network-how-myzubster-connects-independent-projects-1pmh</guid>
      <description>&lt;h1&gt;
  
  
  Building an Evidence-First Contributor Network: How MyZubster Connects Independent Projects
&lt;/h1&gt;

&lt;p&gt;Open-source collaboration becomes fragile when contribution history, current responsibilities, public profiles, and external projects all live in different places.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://github.com/MyZubster-Ecosystem/myzubster" rel="noopener noreferrer"&gt;MyZubster&lt;/a&gt;, we are working on a simpler boundary:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Contributors keep control of their own repositories and environments.&lt;br&gt;&lt;br&gt;
MyZubster connects only the evidence, interfaces, and explicitly approved collaboration checkpoints.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is not a claim that every contributor belongs to a DAO, has a public digital passport, or is part of a funded research consortium. Those are separate states and must remain separate.&lt;/p&gt;

&lt;p&gt;Our public README now includes a compact status index for contributor roles, digital-passport publication, external projects, and LIFE-oriented pilot candidates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1606" rel="noopener noreferrer"&gt;Contributor status index PR&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1605" rel="noopener noreferrer"&gt;Contributor Operations v1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1505" rel="noopener noreferrer"&gt;Pilot Node Network&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The model
&lt;/h2&gt;

&lt;p&gt;A connected contributor project follows this path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;independent repository or environment
→ public, bounded evidence
→ documented interface or fixture
→ reproducible test
→ reviewed result
→ optional Knowledge / Passport / pilot link
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important words are &lt;em&gt;bounded&lt;/em&gt;, &lt;em&gt;optional&lt;/em&gt;, and &lt;em&gt;reviewed&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A GitHub fork is not automatically a competence credential. A merged pull request is not automatically a public Passport. A pilot is not automatically a partnership. And a LIFE-oriented internal track is not an EU LIFE grant or consortium.&lt;/p&gt;

&lt;h2&gt;
  
  
  The contributors and linked projects
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Daniel Ioni — coordination and evidence boundaries
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/danieldirimini-myzubster" rel="noopener noreferrer"&gt;@danieldirimini-myzubster&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Core project: &lt;a href="https://github.com/MyZubster-Ecosystem/myzubster" rel="noopener noreferrer"&gt;MyZubster&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Current scope: interim product and governance coordination, privacy boundaries, acceptance criteria, and evidence architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The role is deliberately limited: coordination is not unilateral DAO authority. High-impact actions such as public-profile publication, treasury activity, deployment, funding, or institutional commitments require their own explicit process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nicola / N4K48 — independent Docker node and interoperability reference
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/nicolaususnicola-lgtm" rel="noopener noreferrer"&gt;@nicolaususnicola-lgtm&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Project: &lt;a href="https://github.com/nicolaususnicola-lgtm/myzubster-mvp" rel="noopener noreferrer"&gt;myzubster-mvp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Reference: &lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1474" rel="noopener noreferrer"&gt;independent pilot-node path&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nicola’s work is the clearest example of the independent-project model. The N4K48 environment remains independently operated, while published Docker, provenance, and read-only interoperability evidence can be reviewed by others.&lt;/p&gt;

&lt;p&gt;The next proposed checkpoint is an authenticated, sanitized N4K48 ↔ MyZubster test. It is a technical interoperability task—not a transfer of ownership, a production deployment, or a LIFE membership claim.&lt;/p&gt;

&lt;h3&gt;
  
  
  khongten124 — Open Period Care research and evidence package
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/khongten124" rel="noopener noreferrer"&gt;@khongten124&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Evidence: &lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1451" rel="noopener noreferrer"&gt;PR #1451&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Project documentation: &lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/tree/main/docs/pilots/open-period-care" rel="noopener noreferrer"&gt;Open Period Care package&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Network reference: &lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1505" rel="noopener noreferrer"&gt;Pilot Node Network #1505&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Open Period Care work is connected as a research/evidence package. The contribution has a bounded evidence state and can be reused as a reference for provenance, structured requirements, and reproducible review.&lt;/p&gt;

&lt;p&gt;It does not claim clinical validation, product efficacy, institutional endorsement, or a formal LIFE role.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aming9303 — gateway, sensor, replication, and signed-webhook evidence
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/Aming9303" rel="noopener noreferrer"&gt;@Aming9303&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Project fork: &lt;a href="https://github.com/Aming9303/MyZubsterGateway" rel="noopener noreferrer"&gt;MyZubsterGateway&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Evidence:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/859" rel="noopener noreferrer"&gt;sensor adapter PR #859&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/861" rel="noopener noreferrer"&gt;replication validator PR #861&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/891" rel="noopener noreferrer"&gt;signed webhooks PR #891&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aming’s public work contributes to the technical edge of the network: adapters, replication checks, and signed webhook behavior. These are useful building blocks for future environmental or operational pilots, including Circular Water, but they do not imply access to a partner’s systems or data.&lt;/p&gt;

&lt;h3&gt;
  
  
  wasim-builds — security and local resilience design
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/wasim-builds" rel="noopener noreferrer"&gt;@wasim-builds&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Evidence:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/860" rel="noopener noreferrer"&gt;security regression PR #860&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1513" rel="noopener noreferrer"&gt;onboarding fix PR #1513&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1584" rel="noopener noreferrer"&gt;local Tor/HAProxy pilot #1584&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wasim has opted into a strictly local and isolated resilience-design track. The planned deliverable is a draft PR containing a threat model, Compose configuration, health checks, rollback instructions, and redacted smoke-test evidence.&lt;/p&gt;

&lt;p&gt;There is no authorization for a public Onion deployment, production access, reimbursement, or paid bounty in that scope.&lt;/p&gt;

&lt;h3&gt;
  
  
  foxxx009 — documentation, KPI, and evidence framework
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/foxxx009" rel="noopener noreferrer"&gt;@foxxx009&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation repository: &lt;a href="https://github.com/foxxx009/myzubster-docs" rel="noopener noreferrer"&gt;myzubster-docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Marketplace repository: &lt;a href="https://github.com/foxxx009/MyZubster-Marketplace" rel="noopener noreferrer"&gt;MyZubster-Marketplace&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Evidence:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/894" rel="noopener noreferrer"&gt;KPI/evidence framework PR #894&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/259" rel="noopener noreferrer"&gt;GitHub monitor test PR #259&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1526" rel="noopener noreferrer"&gt;re-enable fix PR #1526&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;foxxx009 has consented to existing public graph links. That is not the same as accepting a new project assignment. Their evidence is useful for future independent verification and KPI work, but any new pilot must start with a separate opt-in and a defined test plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shweta Singh — technical verifier path, still consent pending
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/Shweta-singh24" rel="noopener noreferrer"&gt;@Shweta-singh24&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Project fork: &lt;a href="https://github.com/Shweta-singh24/MyZubsterGateway" rel="noopener noreferrer"&gt;MyZubsterGateway&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Reference: &lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1505" rel="noopener noreferrer"&gt;Pilot Node Network #1505&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Shweta’s public fork has been discovered, but a fork alone is not enough to claim a verified contributor role. The proposed path is technical verification: Docker, Node.js, HTTPS/read-only interoperability, documentation, and contributor-to-contributor reproduction.&lt;/p&gt;

&lt;p&gt;That path remains opt-in and evidence pending.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the digital Passport means
&lt;/h2&gt;

&lt;p&gt;A MyZubster Contributor Passport is not an automatic profile created from a GitHub username.&lt;/p&gt;

&lt;p&gt;A public Passport requires explicit publication consent. It should expose only an approved, minimized public projection of contribution evidence. It must not expose internal reviewer data, payment identifiers, reward amounts, settlement records, secrets, or sensitive personal information.&lt;/p&gt;

&lt;p&gt;This is an active privacy requirement, tracked in &lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1598" rel="noopener noreferrer"&gt;issue #1598&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What “LIFE-oriented” means here
&lt;/h2&gt;

&lt;p&gt;MyZubster has candidate real-world tracks such as kefir, Circular Water, and circular-care evidence. They can reuse technical components created by independent contributors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker and independent-node operation;&lt;/li&gt;
&lt;li&gt;data schemas and deterministic hashes;&lt;/li&gt;
&lt;li&gt;read-only adapters;&lt;/li&gt;
&lt;li&gt;KPI dashboards;&lt;/li&gt;
&lt;li&gt;evidence review and provenance;&lt;/li&gt;
&lt;li&gt;privacy and consent controls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the language must stay precise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a candidate is not a consortium member;&lt;/li&gt;
&lt;li&gt;a technical pilot is not a validated field pilot;&lt;/li&gt;
&lt;li&gt;a company reviewing a concept is not yet a project partner;&lt;/li&gt;
&lt;li&gt;an internal LIFE-oriented track is not EU LIFE funding.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Circular Water, the correct approach is to build reusable, synthetic-data components first. If a water utility later authorizes a real case, only the authorized data, constraints, and measurements are connected to the shared framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this structure matters
&lt;/h2&gt;

&lt;p&gt;The goal is not to centralize everyone’s work under one account or one server.&lt;/p&gt;

&lt;p&gt;The goal is to make collaboration inspectable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;who owns the project?
what was actually tested?
what evidence is public?
what is proposed versus accepted?
what consent exists?
what can be reused safely?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is how a contributor network becomes credible: not by making the biggest possible claim, but by preserving the boundary between evidence, consent, current work, and future ambition.&lt;/p&gt;

&lt;p&gt;If you want to contribute, start with a small bounded task, publish reproducible evidence, and keep control of your own work. The network can connect it when both the technical and consent conditions are satisfied.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster" rel="noopener noreferrer"&gt;MyZubster core repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1605" rel="noopener noreferrer"&gt;Contributor Operations v1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1505" rel="noopener noreferrer"&gt;Pilot Node Network&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues" rel="noopener noreferrer"&gt;Open contributor issues&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>contributor</category>
      <category>network</category>
      <category>myzubster</category>
      <category>connects</category>
    </item>
    <item>
      <title>Hardhat 3 in a CommonJS Application: Isolating ESM Tooling Without Breaking CI</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Tue, 13 Oct 2026 04:28:50 +0000</pubDate>
      <link>https://dev.to/danielioni/hardhat-3-in-a-commonjs-application-isolating-esm-tooling-without-breaking-ci-1mh8</link>
      <guid>https://dev.to/danielioni/hardhat-3-in-a-commonjs-application-isolating-esm-tooling-without-breaking-ci-1mh8</guid>
      <description>&lt;h1&gt;
  
  
  Hardhat 3 in a CommonJS Application: Isolating ESM Tooling Without Breaking CI
&lt;/h1&gt;

&lt;p&gt;Modernising blockchain tooling inside an established application is rarely just a dependency upgrade.&lt;/p&gt;

&lt;p&gt;Our repository is primarily a CommonJS Node.js application: the server, most tests, and existing runtime scripts use &lt;code&gt;require()&lt;/code&gt;. At the same time, we needed to move the NFT toolchain to Hardhat 3, which is ESM-first. A naïve migration—setting &lt;code&gt;"type": "module"&lt;/code&gt; at the repository root—would have changed the module semantics of the whole application and created unnecessary runtime risk.&lt;/p&gt;

&lt;p&gt;This article documents the approach we used instead: isolate the ESM toolchain, make mixed test execution deterministic, remove a high-severity dependency path, and verify the result without deploying anything to a public chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The constraints
&lt;/h2&gt;

&lt;p&gt;The change had four non-negotiable constraints:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The existing CommonJS application had to remain CommonJS.&lt;/li&gt;
&lt;li&gt;Solidity contracts and NFT scripts needed to run under Hardhat 3.&lt;/li&gt;
&lt;li&gt;CommonJS Jest tests and ESM Node tests needed to run correctly in both local development and CI.&lt;/li&gt;
&lt;li&gt;Dependency auditing had to fail on high and critical vulnerabilities without relying on unsafe automatic upgrades.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The goal was not to “make the build pass” at any cost. The goal was to make the boundaries explicit and testable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why converting the repository root was the wrong move
&lt;/h2&gt;

&lt;p&gt;Hardhat 3 expects an ESM project. The obvious reaction is to add this to the root &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That would make every &lt;code&gt;.js&lt;/code&gt; file in the repository ESM by default. In a mature CommonJS application, that affects server startup, test helpers, scripts, configuration files, and dependencies that expect &lt;code&gt;require()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The blockchain toolchain has different module requirements from the application runtime. Treating them as one module system turns a local migration into a repository-wide compatibility project.&lt;/p&gt;

&lt;p&gt;The safer design was to create a narrow ESM boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an isolated ESM workspace
&lt;/h2&gt;

&lt;p&gt;We moved Solidity-specific tooling into a dedicated directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nft-tools/
  package.json
  hardhat.config.js
  contracts/
  scripts/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its &lt;code&gt;package.json&lt;/code&gt; is intentionally small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@myzubster/nft-tools"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The root application remains CommonJS. Only the NFT workspace is ESM.&lt;/p&gt;

&lt;p&gt;This lets the Hardhat configuration use native imports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hardhat/config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;hardhatEthers&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@nomicfoundation/hardhat-ethers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hardhatEthers&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;solidity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.8.28&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./contracts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./cache&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./artifacts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The separation is more than cosmetic. Hardhat resolves project files relative to its project boundary, so contracts must live inside that ESM workspace rather than outside it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrate scripts deliberately
&lt;/h2&gt;

&lt;p&gt;The previous NFT scripts used CommonJS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hre&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hardhat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the isolated ESM workspace, they now use the Hardhat 3 runtime model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hardhat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOrCreate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSigners&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scripts retain their original safety characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deployment and minting remain explicit commands;&lt;/li&gt;
&lt;li&gt;public-chain operations require an external private-key environment variable;&lt;/li&gt;
&lt;li&gt;local tests use Hardhat’s simulated network;&lt;/li&gt;
&lt;li&gt;no real transaction is sent during compilation or local verification.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Root-level scripts make the intended boundary visible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nft:compile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd nft-tools &amp;amp;&amp;amp; ../node_modules/.bin/hardhat compile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nft:test:local"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd nft-tools &amp;amp;&amp;amp; ../node_modules/.bin/hardhat run scripts/test-comic-nft.mjs"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A local NFT test deploys the contract, mints a test token, and verifies ownership and metadata URI. It does not use a wallet, a public RPC account, or production funds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency audit: avoid destructive “fixes”
&lt;/h2&gt;

&lt;p&gt;During the audit, the high-severity issue was not application code. It came from the development watcher dependency path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nodemon → chokidar → braces
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An automated &lt;code&gt;npm audit fix --force&lt;/code&gt; proposed breaking downgrades, including old versions of development tooling. That is not a reliable security remediation strategy.&lt;/p&gt;

&lt;p&gt;Instead, we removed the unnecessary watcher dependency and used Node’s built-in watch mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node --watch server.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dependency audit then had no high or critical findings. Moderate findings remain in Jest coverage-related transitive dependencies and are documented rather than hidden. The CI gate continues to fail on high or critical findings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm audit &lt;span class="nt"&gt;--audit-level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;high
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Security work should reduce exposure without silently introducing a different operational risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mixed CommonJS and ESM tests need two runners
&lt;/h2&gt;

&lt;p&gt;The repository contains two valid kinds of tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a large Jest suite for CommonJS application tests;&lt;/li&gt;
&lt;li&gt;a small set of &lt;code&gt;.test.mjs&lt;/code&gt; files that must run through Node’s native ESM test runner.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Jest can discover &lt;code&gt;.mjs&lt;/code&gt; files, but attempting to execute them as CommonJS produces errors such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Must use import to load ES Module
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The solution is not to force ESM tests through a CommonJS runner. Instead, the test command is an explicit two-stage process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;spawnSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:child_process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;esmTests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identity/credential/credential.test.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identity/verifier/verify.test.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identity/hash-verification/verify-hash.test.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;workshop/app/domain.test.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;workshop/app/integrate.test.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;spawnSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;stdio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inherit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jest is restricted to JavaScript test files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;testMatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;**/?(*.)+(test).js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the ESM files run separately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;esmTests&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives each test type the runtime it actually requires.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CI argument-forwarding trap
&lt;/h2&gt;

&lt;p&gt;One subtle failure only appeared in CI.&lt;/p&gt;

&lt;p&gt;The workflow called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--runInBand&lt;/span&gt; &lt;span class="nt"&gt;--testPathIgnorePatterns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/frontend/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jest command-line options can override settings declared in &lt;code&gt;jest.config.js&lt;/code&gt;. In this case, the workflow’s &lt;code&gt;testPathIgnorePatterns&lt;/code&gt; replaced the repository configuration, allowing Jest to discover the ESM test files again.&lt;/p&gt;

&lt;p&gt;The wrapper now filters that externally supplied ignore-pattern override before invoking Jest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jestArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--testPathIgnorePatterns=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jest/bin/jest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;jestArgs&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository configuration remains the source of truth for test discovery; CI still passes useful execution options such as &lt;code&gt;--runInBand&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is a general lesson: forwarding CLI arguments blindly can change test semantics. A wrapper should forward only the arguments that are safe for that stage of the pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification results
&lt;/h2&gt;

&lt;p&gt;The final verification was intentionally layered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Jest:                 221 suites / 969 tests passed
Node ESM runner:       26 tests passed
Hardhat compile:       4 Solidity contracts compiled
Local NFT test:        deploy + mint + ownership + URI passed
Security audit:        no high or critical vulnerabilities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No deployment and no public-chain transaction were performed.&lt;/p&gt;

&lt;p&gt;The pull request also passed the repository’s test, evidence, policy, security-audit, and secret-scanning checks. A Vercel status remained blocked by external deployment authorization, so it was treated as an environment-integration issue—not as evidence that the code had been deployed or independently validated.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this migration changed
&lt;/h2&gt;

&lt;p&gt;The important result is not simply “Hardhat 3 works.”&lt;/p&gt;

&lt;p&gt;The repository now has clearer operational boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the application runtime remains CommonJS;&lt;/li&gt;
&lt;li&gt;blockchain tooling is isolated in an ESM workspace;&lt;/li&gt;
&lt;li&gt;CommonJS and ESM tests run with their native runners;&lt;/li&gt;
&lt;li&gt;CI argument handling cannot accidentally re-enable incompatible test discovery;&lt;/li&gt;
&lt;li&gt;local blockchain verification is explicit and does not touch public networks;&lt;/li&gt;
&lt;li&gt;high- and critical-severity audit findings are gated without unsafe forced upgrades.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tooling migrations are safest when they reduce ambiguity. In this case, separating module systems, test runners, and deployment intent made the codebase easier to verify—and harder to accidentally misuse.&lt;/p&gt;

</description>
      <category>hardhat</category>
      <category>commonjs</category>
      <category>myzubster</category>
      <category>esm</category>
    </item>
    <item>
      <title>Building the MyZubster Metaverse: Browser-First WebXR, Server-Authoritative Rooms, Versioned Scenes, and the Road Ahead</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Mon, 12 Oct 2026 13:21:01 +0000</pubDate>
      <link>https://dev.to/danielioni/building-the-myzubster-metaverse-browser-first-webxr-server-authoritative-rooms-versioned-5f8f</link>
      <guid>https://dev.to/danielioni/building-the-myzubster-metaverse-browser-first-webxr-server-authoritative-rooms-versioned-5f8f</guid>
      <description>&lt;p&gt;Building the MyZubster Metaverse: Browser-First WebXR, Server-Authoritative Rooms, Versioned Scenes, and the Road Ahead&lt;br&gt;
The MyZubster Metaverse is evolving from a narrative prototype into a browser-first, testable platform with explicit technical contracts.&lt;br&gt;
The objective is not to build a VR-only experience. The objective is to create a shared digital environment that works on ordinary browsers first, can progressively enable WebXR when available, and can connect virtual experiences to real MyZubster projects—including LIFE-oriented environmental pilots.&lt;br&gt;
This technical update covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What currently works&lt;/li&gt;
&lt;li&gt;What we verified in production&lt;/li&gt;
&lt;li&gt;The browser and WebXR capability layer&lt;/li&gt;
&lt;li&gt;The server-authoritative room lifecycle&lt;/li&gt;
&lt;li&gt;The versioned scene-manifest contract&lt;/li&gt;
&lt;li&gt;Security and validation boundaries&lt;/li&gt;
&lt;li&gt;How this connects to LIFE and evidence-based pilots&lt;/li&gt;
&lt;li&gt;What is still missing&lt;/li&gt;
&lt;li&gt;The next implementation milestones
Repository: MyZubster-Ecosystem/myzubster
Live entry point: MyZubster Metaverse&lt;/li&gt;
&lt;li&gt;The Architecture We Are Building
The current direction separates the metaverse into several technical layers:
┌───────────────────────────────────────────────────────────┐
│                    Browser Experience                     │
│                                                           │
│  React UI → capability checks → browser/WebXR fallback    │
└───────────────────────────────┬───────────────────────────┘
                            │
                            ▼
┌───────────────────────────────────────────────────────────┐
│                   Metaverse Client Runtime                │
│                                                           │
│  Scene loading → controls → chat → emotes → interactions  │
└───────────────────────────────┬───────────────────────────┘
                            │
                            ▼
┌───────────────────────────────────────────────────────────┐
│                  Server-Authoritative API                 │
│                                                           │
│  Rooms → sessions → access → capacity → stage → tokens    │
└───────────────────────────────┬───────────────────────────┘
                            │
                            ▼
┌───────────────────────────────────────────────────────────┐
│                 Versioned Scene Contract                  │
│                                                           │
│  Assets → spawn points → zones → portals → capabilities   │
└───────────────────────────────┬───────────────────────────┘
                            │
                            ▼
┌───────────────────────────────────────────────────────────┐
│                    Evidence / LIFE Layer                  │
│                                                           │
│  Pilot → provenance → evidence → KPI/MRV → visualization  │
└───────────────────────────────────────────────────────────┘
Each layer has a different responsibility.
The browser detects what the device can do. The server controls room and session authority. The scene manifest defines what should be loaded. The evidence layer determines what claims can legitimately be represented.
This separation matters because a 3D scene must not become the source of truth for authentication, permissions, environmental claims, or evidence.&lt;/li&gt;
&lt;li&gt;What Works Today
Neon Plaza is available directly from a browser. A VR headset, crypto wallet, native application, or dedicated GPU is not required for the standard experience.
During our production desktop smoke test, we verified:&lt;/li&gt;
&lt;li&gt;Authenticated access&lt;/li&gt;
&lt;li&gt;Character-associated entry&lt;/li&gt;
&lt;li&gt;Neon Plaza loading&lt;/li&gt;
&lt;li&gt;An active online session&lt;/li&gt;
&lt;li&gt;Movement controls&lt;/li&gt;
&lt;li&gt;Emotes&lt;/li&gt;
&lt;li&gt;Room chat&lt;/li&gt;
&lt;li&gt;Browser capability detection&lt;/li&gt;
&lt;li&gt;A non-blocking fallback when immersive WebXR is unavailable
The critical product rule is:
WebXR is an optional capability. It must never become a mandatory gateway to the MyZubster Metaverse.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Desktop and mobile browsers remain the baseline. VR devices can progressively enhance the experience when supported.&lt;br&gt;
This is important for accessibility, adoption, testing, and compatibility with real-world projects. A LIFE participant, researcher, local community member, or developer should not need a headset simply to inspect a pilot, join a session, or understand a project.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Browser and WebXR Capability Detection
The first implementation milestone was tracked in:&lt;/li&gt;
&lt;li&gt;PR #1597 — Add non-blocking WebXR capability fallback
The browser capability module defines explicit WebXR states:
export const WEBXR_STATUS = Object.freeze({
CHECKING: 'checking',
SUPPORTED: 'supported',
UNSUPPORTED: 'unsupported',
ERROR: 'error'
});
This is intentionally more precise than a boolean such as hasWebXR.
There are several meaningful conditions:
checking
│
├── supported
│
├── unsupported
│
└── error
An unsupported API is different from a capability check that failed. A browser without navigator.xr is different from a browser exposing the API but rejecting the request.
The implementation checks the browser baseline separately:
export function browserCapabilityBaseline(browser = window) {
return [
{ label: 'JavaScript', ok: true },
{ label: 'Fetch API', ok: typeof browser.fetch === 'function' },
{ label: 'Storage locale', ok: Boolean(browser.localStorage) },
{
  label: 'CSS Grid',
  ok: Boolean(browser.CSS?.supports?.('display', 'grid'))
},
{
  label: 'Connessione',
  ok: browser.navigator?.onLine !== false
}
];
}
WebXR support is detected asynchronously:
export async function detectImmersiveVr(
navigatorLike = navigator
) {
if (!navigatorLike?.xr?.isSessionSupported) {
return {
  status: WEBXR_STATUS.UNSUPPORTED,
  reason: 'api-unavailable'
};
}&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;try {&lt;br&gt;
    const supported =&lt;br&gt;
      await navigatorLike.xr.isSessionSupported('immersive-vr');&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return {
  status: supported
    ? WEBXR_STATUS.SUPPORTED
    : WEBXR_STATUS.UNSUPPORTED,
  reason: supported ? null : 'session-unsupported'
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} catch (_error) {&lt;br&gt;
    return {&lt;br&gt;
      status: WEBXR_STATUS.ERROR,&lt;br&gt;
      reason: 'capability-check-failed'&lt;br&gt;
    };&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
Why dependency injection matters here&lt;br&gt;
The function accepts navigatorLike instead of directly depending on the global navigator internally.&lt;br&gt;
That makes capability behavior testable:&lt;br&gt;
await detectImmersiveVr({&lt;br&gt;
  xr: {&lt;br&gt;
    isSessionSupported: async () =&amp;gt; true&lt;br&gt;
  }&lt;br&gt;
});&lt;br&gt;
It also allows us to test unsupported and error conditions without requiring an actual headset in every CI run.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Non-Blocking Fallback UX
Capability detection is connected to a React component that communicates the system state to the user.
A simplified version of the state mapping is:
const XR_COPY = {
checking: 'Checking WebXR support…',
supported: 'Immersive WebXR is available',
unsupported:
'Browser mode active: a headset is not required',
error:
'WebXR could not be verified: continue in browser mode'
};
The important behavior is not just the message. It is the absence of a blocking redirect or fatal error.
const browserReady =
checks.every((check) =&amp;gt; check.ok);&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;const ready =&lt;br&gt;
  browserReady &amp;amp;&amp;amp;&lt;br&gt;
  xr.status !== WEBXR_STATUS.CHECKING;&lt;br&gt;
When immersive VR is unavailable, the interface explicitly keeps the browser path open:&lt;br&gt;
{xr.status !== WEBXR_STATUS.SUPPORTED &amp;amp;&amp;amp; (&lt;br&gt;
  &lt;small&gt;&lt;br&gt;
    Neon Plaza remains accessible from desktop and mobile.&lt;br&gt;
    The WebXR check never blocks entry.&lt;br&gt;
  &lt;/small&gt;&lt;br&gt;
)}&lt;br&gt;
This approach supports progressive enhancement:&lt;br&gt;
Basic browser&lt;br&gt;
    ↓&lt;br&gt;
Neon Plaza browser experience&lt;br&gt;
    ↓&lt;br&gt;
Optional advanced capabilities&lt;br&gt;
    ├── richer graphics&lt;br&gt;
    ├── immersive WebXR&lt;br&gt;
    ├── spatial input&lt;br&gt;
    └── device-specific controls&lt;br&gt;
The baseline must remain usable even when one of the enhancement layers is unavailable.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Server-Authoritative Room Lifecycle
The metaverse is not controlled only by client-side React state.
Rooms and sessions have server-authoritative lifecycle states:
const ROOM_TRANSITIONS = Object.freeze({
draft: new Set(['published']),
published: new Set(['scheduled', 'live']),
scheduled: new Set(['live']),
live: new Set(['ended']),
ended: new Set(['archive']),
archive: new Set([])
});
This gives us a forward-only state machine:
draft
│
▼
published
│
├───────────────┐
▼               ▼
scheduled        live
│               │
└───────►───────┘
              ▼
            ended
              │
              ▼
           archive
Invalid reverse transitions are rejected.
For example:
if (
patch.state &amp;amp;&amp;amp;
patch.state !== room.state
) {
if (!ROOM_TRANSITIONS[room.state]?.has(patch.state)) {
return {
  valid: false,
  status: 409,
  error:
    &lt;code&gt;Invalid room transition&lt;/code&gt; +
    &lt;code&gt;${room.state} -&amp;gt; ${patch.state}&lt;/code&gt;
};
}&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;room.state = patch.state;&lt;br&gt;
}&lt;br&gt;
This prevents a browser client from arbitrarily changing an ended room back to live.&lt;br&gt;
The client requests a transition. The server decides whether it is valid.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Room Access and Capacity
A virtual room can currently use one of three access policies:
accessPolicy: {
type: String,
enum: [
'public',
'authenticated',
'private'
],
default: 'authenticated'
}
Capacity is bounded at the model and service layers:
capacity: {
type: Number,
min: 1,
max: 500,
default: 100
}
Join validation checks:&lt;/li&gt;
&lt;li&gt;Session state&lt;/li&gt;
&lt;li&gt;Blocklist&lt;/li&gt;
&lt;li&gt;Private-room allowlist&lt;/li&gt;
&lt;li&gt;Host access&lt;/li&gt;
&lt;li&gt;Current participant count&lt;/li&gt;
&lt;li&gt;Existing membership
async function validateJoin({
session,
room,
actorUserId
}) {
if (session.state !== 'live') {
return {
  valid: false,
  status: 409,
  error: 'Session is not live'
};
}&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;const actor = String(actorUserId);&lt;/p&gt;

&lt;p&gt;if ((room.blockedUserIds || []).includes(actor)) {&lt;br&gt;
    return {&lt;br&gt;
      valid: false,&lt;br&gt;
      status: 403,&lt;br&gt;
      error: 'Access blocked'&lt;br&gt;
    };&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;if (&lt;br&gt;
    room.accessPolicy === 'private' &amp;amp;&amp;amp;&lt;br&gt;
    !(room.allowedUserIds || []).includes(actor) &amp;amp;&amp;amp;&lt;br&gt;
    actor !== String(room.hostUserId)&lt;br&gt;
  ) {&lt;br&gt;
    return {&lt;br&gt;
      valid: false,&lt;br&gt;
      status: 403,&lt;br&gt;
      error: 'Private room access denied'&lt;br&gt;
    };&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;const current =&lt;br&gt;
    new Set(session.participantUserIds || []);&lt;/p&gt;

&lt;p&gt;if (&lt;br&gt;
    !current.has(actor) &amp;amp;&amp;amp;&lt;br&gt;
    current.size &amp;gt;= session.capacity&lt;br&gt;
  ) {&lt;br&gt;
    return {&lt;br&gt;
      valid: false,&lt;br&gt;
      status: 409,&lt;br&gt;
      error: 'Session is at capacity'&lt;br&gt;
    };&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;return { valid: true };&lt;br&gt;
}&lt;br&gt;
The server therefore remains authoritative over membership. A modified browser cannot legitimately join a closed or full session simply by changing local state.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Short-Lived Realtime Tokens
After a successful join, the backend can mint a short-lived realtime token.
The token is scoped to:&lt;/li&gt;
&lt;li&gt;The authenticated user&lt;/li&gt;
&lt;li&gt;A specific purpose&lt;/li&gt;
&lt;li&gt;A session&lt;/li&gt;
&lt;li&gt;A room&lt;/li&gt;
&lt;li&gt;A scene-manifest version
function mintRealtimeToken(
session,
actorUserId
) {
const secret =
process.env.REALTIME_TOKEN_SECRET ||
process.env.JWT_SECRET;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;if (!secret) {&lt;br&gt;
    throw new Error(&lt;br&gt;
      'Realtime token secret is not configured'&lt;br&gt;
    );&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;return jwt.sign({&lt;br&gt;
    sub: String(actorUserId),&lt;br&gt;
    purpose: 'metaverse-realtime',&lt;br&gt;
    sessionId: session.sessionId,&lt;br&gt;
    roomId: session.roomId,&lt;br&gt;
    sceneManifestVersion:&lt;br&gt;
      session.sceneManifestVersion&lt;br&gt;
  }, secret, {&lt;br&gt;
    expiresIn: '5m'&lt;br&gt;
  });&lt;br&gt;
}&lt;br&gt;
The five-minute expiration limits the lifetime of captured tokens.&lt;br&gt;
Future work will need to evaluate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token refresh&lt;/li&gt;
&lt;li&gt;Revocation semantics&lt;/li&gt;
&lt;li&gt;Horizontal realtime scaling&lt;/li&gt;
&lt;li&gt;Redis-backed session coordination&lt;/li&gt;
&lt;li&gt;Replay resistance&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;Reconnect behavior&lt;/li&gt;
&lt;li&gt;Multi-region latency&lt;/li&gt;
&lt;li&gt;Moderation Without Exposing Raw Account IDs
Moderation references are generated as opaque identifiers rather than exposing account IDs directly in public session views.
function moderationParticipantRef(
sessionId,
actorUserId
) {
return hashRoomInviteCode(
&lt;code&gt;${sessionId}:${actorUserId}&lt;/code&gt;
).slice(0, 24);
}
Room blocklist references are scoped separately:
function blockedParticipantRef(
roomId,
actorUserId
) {
return hashRoomInviteCode(
&lt;code&gt;blocklist:${roomId}:${actorUserId}&lt;/code&gt;
).slice(0, 24);
}
This does not make the system anonymous by itself, but it reduces unnecessary identifier exposure in moderation interfaces.
The server supports actions such as:&lt;/li&gt;
&lt;li&gt;Removing a participant from a live session&lt;/li&gt;
&lt;li&gt;Blocking a participant from a room&lt;/li&gt;
&lt;li&gt;Removing pending stage requests&lt;/li&gt;
&lt;li&gt;Revoking speaker status&lt;/li&gt;
&lt;li&gt;Listing blocklist entries through opaque references
The public session representation also excludes raw participant arrays:
function publicSession(session) {
return {
id: session.sessionId,
roomId: session.roomId,
state: session.state,
capacity: session.capacity,
participantCount:
  (session.participantUserIds || []).length,
sceneManifestVersion:
  session.sceneManifestVersion,
lifecycleVersion:
  session.lifecycleVersion,
startedAt: session.startedAt || null,
endedAt: session.endedAt || null
};
}&lt;/li&gt;
&lt;li&gt;Versioned Scene Manifest v1
The second major implementation milestone was delivered through:&lt;/li&gt;
&lt;li&gt;PR #1601 — Validate versioned scene manifests before publication
The manifest establishes an explicit contract between the room configuration, asset pipeline, backend, and client runtime.
The current schema version is:
const SCENE_MANIFEST_SCHEMA_VERSION = '1';
Supported asset types are intentionally constrained:
const ASSET_TYPES = new Set([
'gltf',
'glb',
'image',
'audio',
'video'
]);
The initial contract also defines allowed zone types, lighting presets, and capability requirements:
const ZONE_TYPES = new Set([
'portal',
'stage',
'link'
]);&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;const LIGHTING_PRESETS = new Set([&lt;br&gt;
  'day',&lt;br&gt;
  'sunset',&lt;br&gt;
  'night',&lt;br&gt;
  'studio'&lt;br&gt;
]);&lt;/p&gt;

&lt;p&gt;const CAPABILITIES = new Set([&lt;br&gt;
  'webgl2',&lt;br&gt;
  'webxr-immersive-vr',&lt;br&gt;
  'audio',&lt;br&gt;
  'video'&lt;br&gt;
]);&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Safe Default Manifest&lt;br&gt;
Every newly created room receives a minimal valid scene manifest:&lt;br&gt;
function defaultSceneManifest() {&lt;br&gt;
return {&lt;br&gt;
schemaVersion:&lt;br&gt;
  SCENE_MANIFEST_SCHEMA_VERSION,&lt;/p&gt;

&lt;p&gt;assetBundleVersion: '1',&lt;/p&gt;

&lt;p&gt;assets: [],&lt;/p&gt;

&lt;p&gt;spawnPoints: [&lt;br&gt;
  {&lt;br&gt;
    id: 'default',&lt;br&gt;
    position: [0, 0, 0]&lt;br&gt;
  }&lt;br&gt;
],&lt;/p&gt;

&lt;p&gt;interactionZones: [],&lt;/p&gt;

&lt;p&gt;stage: {&lt;br&gt;
  position: [0, 0, 0],&lt;br&gt;
  capacity: 1&lt;br&gt;
},&lt;/p&gt;

&lt;p&gt;lighting: {&lt;br&gt;
  preset: 'day'&lt;br&gt;
},&lt;/p&gt;

&lt;p&gt;capabilities: []&lt;br&gt;
};&lt;br&gt;
}&lt;br&gt;
This provides a known baseline rather than leaving newly created rooms in an undefined state.&lt;br&gt;
The room creation service persists this default:&lt;br&gt;
const room = await VirtualRoom.create({&lt;br&gt;
roomId: crypto.randomUUID(),&lt;br&gt;
slug: safeSlug,&lt;br&gt;
name: safeName,&lt;br&gt;
hostUserId: String(actorUserId),&lt;br&gt;
accessPolicy,&lt;br&gt;
capacity: numericCapacity,&lt;br&gt;
stagePolicy,&lt;br&gt;
sceneManifest: defaultSceneManifest()&lt;br&gt;
});&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Example of a More Complete Scene Manifest&lt;br&gt;
A future Neon Plaza manifest could look like this:&lt;br&gt;
{&lt;br&gt;
"schemaVersion": "1",&lt;br&gt;
"assetBundleVersion": "42",&lt;br&gt;
"assets": [&lt;br&gt;
{&lt;br&gt;
  "id": "neon-plaza",&lt;br&gt;
  "type": "glb",&lt;br&gt;
  "url": "&lt;a href="https://cdn.myzubster.com/scenes/neon-plaza.42.glb" rel="noopener noreferrer"&gt;https://cdn.myzubster.com/scenes/neon-plaza.42.glb&lt;/a&gt;"&lt;br&gt;
},&lt;br&gt;
{&lt;br&gt;
  "id": "ambient-track",&lt;br&gt;
  "type": "audio",&lt;br&gt;
  "url": "&lt;a href="https://cdn.myzubster.com/audio/neon-plaza.7.ogg" rel="noopener noreferrer"&gt;https://cdn.myzubster.com/audio/neon-plaza.7.ogg&lt;/a&gt;"&lt;br&gt;
},&lt;br&gt;
{&lt;br&gt;
  "id": "life-dashboard",&lt;br&gt;
  "type": "image",&lt;br&gt;
  "url": "&lt;a href="https://cdn.myzubster.com/life/pilot-dashboard.12.webp" rel="noopener noreferrer"&gt;https://cdn.myzubster.com/life/pilot-dashboard.12.webp&lt;/a&gt;"&lt;br&gt;
}&lt;br&gt;
],&lt;br&gt;
"spawnPoints": [&lt;br&gt;
{&lt;br&gt;
  "id": "main-entrance",&lt;br&gt;
  "position": [0, 1.6, 4]&lt;br&gt;
},&lt;br&gt;
{&lt;br&gt;
  "id": "life-hub",&lt;br&gt;
  "position": [12, 1.6, -3]&lt;br&gt;
}&lt;br&gt;
],&lt;br&gt;
"interactionZones": [&lt;br&gt;
{&lt;br&gt;
  "id": "life-portal",&lt;br&gt;
  "type": "portal",&lt;br&gt;
  "position": [10, 0, -3],&lt;br&gt;
  "radius": 1.5,&lt;br&gt;
  "destination": "life-hub"&lt;br&gt;
},&lt;br&gt;
{&lt;br&gt;
  "id": "pilot-details",&lt;br&gt;
  "type": "link",&lt;br&gt;
  "position": [14, 1, -4],&lt;br&gt;
  "radius": 1,&lt;br&gt;
  "destination": "&lt;a href="https://www.myzubster.com/life-pilot" rel="noopener noreferrer"&gt;https://www.myzubster.com/life-pilot&lt;/a&gt;"&lt;br&gt;
}&lt;br&gt;
],&lt;br&gt;
"stage": {&lt;br&gt;
"position": [0, 0, -8],&lt;br&gt;
"capacity": 30&lt;br&gt;
},&lt;br&gt;
"lighting": {&lt;br&gt;
"preset": "sunset"&lt;br&gt;
},&lt;br&gt;
"capabilities": [&lt;br&gt;
"webgl2",&lt;br&gt;
"audio"&lt;br&gt;
]&lt;br&gt;
}&lt;br&gt;
This is a contract, not an arbitrary JSON document.&lt;br&gt;
The server validates its structure before accepting it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Input Validation and Security Boundaries&lt;br&gt;
Manifest IDs must use a constrained format:&lt;br&gt;
function validId(value) {&lt;br&gt;
return (&lt;br&gt;
typeof value === 'string' &amp;amp;&amp;amp;&lt;br&gt;
/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/.test(value)&lt;br&gt;
);&lt;br&gt;
}&lt;br&gt;
Coordinates must contain exactly three finite values:&lt;br&gt;
function validVector(value) {&lt;br&gt;
return (&lt;br&gt;
Array.isArray(value) &amp;amp;&amp;amp;&lt;br&gt;
value.length === 3 &amp;amp;&amp;amp;&lt;br&gt;
value.every(Number.isFinite)&lt;br&gt;
);&lt;br&gt;
}&lt;br&gt;
Asset and external-link URLs must use HTTPS:&lt;br&gt;
function validHttpsUrl(value) {&lt;br&gt;
if (&lt;br&gt;
typeof value !== 'string' ||&lt;br&gt;
value.length &amp;gt; 2048&lt;br&gt;
) {&lt;br&gt;
return false;&lt;br&gt;
}&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;try {&lt;br&gt;
    return new URL(value).protocol === 'https:';&lt;br&gt;
  } catch {&lt;br&gt;
    return false;&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
This blocks obvious unsafe values such as:&lt;br&gt;
javascript:alert(1)&lt;br&gt;
&lt;a href="http://untrusted.example/asset.glb" rel="noopener noreferrer"&gt;http://untrusted.example/asset.glb&lt;/a&gt;&lt;br&gt;
invalid-url&lt;br&gt;
HTTPS validation alone does not solve every asset-security problem. Future protections should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Approved asset origins&lt;/li&gt;
&lt;li&gt;Content-Type verification&lt;/li&gt;
&lt;li&gt;Maximum file size&lt;/li&gt;
&lt;li&gt;Download timeouts&lt;/li&gt;
&lt;li&gt;Hash or checksum validation&lt;/li&gt;
&lt;li&gt;Malware scanning where applicable&lt;/li&gt;
&lt;li&gt;glTF structural validation&lt;/li&gt;
&lt;li&gt;Texture and geometry limits&lt;/li&gt;
&lt;li&gt;CORS verification&lt;/li&gt;
&lt;li&gt;Redirect restrictions&lt;/li&gt;
&lt;li&gt;Decompression-bomb protections&lt;/li&gt;
&lt;li&gt;Asset-license metadata&lt;/li&gt;
&lt;li&gt;Bounded Collections
The validator limits the number of objects that can be supplied.
For example:
if (
!Array.isArray(input.assets) ||
input.assets.length &amp;gt; 100
) {
errors.push(
'assets must be an array with at most 100 entries'
);
}
Spawn points are also bounded:
if (
!Array.isArray(input.spawnPoints) ||
input.spawnPoints.length &amp;lt; 1 ||
input.spawnPoints.length &amp;gt; 32
) {
errors.push(
'spawnPoints must contain between 1 and 32 entries'
);
}
Bounding collection sizes reduces accidental or malicious resource amplification before scene loading begins.
Client-side budgets will still be needed for:&lt;/li&gt;
&lt;li&gt;Triangle count&lt;/li&gt;
&lt;li&gt;Texture memory&lt;/li&gt;
&lt;li&gt;Audio duration&lt;/li&gt;
&lt;li&gt;Video resolution&lt;/li&gt;
&lt;li&gt;Animation count&lt;/li&gt;
&lt;li&gt;Shader complexity&lt;/li&gt;
&lt;li&gt;Draw calls&lt;/li&gt;
&lt;li&gt;Concurrent network requests&lt;/li&gt;
&lt;li&gt;Duplicate and Invalid Spawn Points
Spawn-point IDs must be valid and unique:
const ids = new Set();&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;input.spawnPoints.forEach((spawn, index) =&amp;gt; {&lt;br&gt;
  if (!isRecord(spawn) || !validId(spawn.id)) {&lt;br&gt;
    errors.push(&lt;br&gt;
      &lt;code&gt;spawnPoints[${index}].id is invalid&lt;/code&gt;&lt;br&gt;
    );&lt;br&gt;
  } else if (ids.has(spawn.id)) {&lt;br&gt;
    errors.push(&lt;br&gt;
      &lt;code&gt;spawnPoints[${index}].id must be unique&lt;/code&gt;&lt;br&gt;
    );&lt;br&gt;
  } else {&lt;br&gt;
    ids.add(spawn.id);&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;if (&lt;br&gt;
    !isRecord(spawn) ||&lt;br&gt;
    !validVector(spawn.position)&lt;br&gt;
  ) {&lt;br&gt;
    errors.push(&lt;br&gt;
      &lt;code&gt;spawnPoints[${index}].position&lt;/code&gt; +&lt;br&gt;
      'must be a finite xyz vector'&lt;br&gt;
    );&lt;br&gt;
  }&lt;br&gt;
});&lt;br&gt;
This prevents ambiguous spawn resolution and invalid coordinates such as:&lt;br&gt;
{&lt;br&gt;
  "id": "entrance",&lt;br&gt;
  "position": [0, null, 4]&lt;br&gt;
}&lt;br&gt;
or:&lt;br&gt;
{&lt;br&gt;
  "id": "entrance",&lt;br&gt;
  "position": [0, 1]&lt;br&gt;
}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Portal and Link Validation
Interaction zones can currently represent:&lt;/li&gt;
&lt;li&gt;Portals&lt;/li&gt;
&lt;li&gt;Stage areas&lt;/li&gt;
&lt;li&gt;External links
Portal destinations must be valid internal identifiers:
if (
zone?.type === 'portal' &amp;amp;&amp;amp;
!validId(zone.destination)
) {
errors.push(
&lt;code&gt;interactionZones[${index}].destination&lt;/code&gt; +
'is required for portals'
);
}
External links must use HTTPS:
if (
zone?.type === 'link' &amp;amp;&amp;amp;
!validHttpsUrl(zone.destination)
) {
errors.push(
&lt;code&gt;interactionZones[${index}].destination&lt;/code&gt; +
'must use https for links'
);
}
A complete implementation will also need to distinguish:
internal room destination
internal MyZubster route
approved external URL
blocked external URL
missing destination
temporarily unavailable destination&lt;/li&gt;
&lt;li&gt;Validation Before Publication
The most important server-side behavior is the publication gate.
When the client supplies a new manifest—or tries to publish a draft—the backend validates the candidate first:
const manifestCandidate =
patch.sceneManifest !== undefined
? patch.sceneManifest
: room.sceneManifest;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;if (&lt;br&gt;
  patch.sceneManifest !== undefined ||&lt;br&gt;
  patch.state === 'published'&lt;br&gt;
) {&lt;br&gt;
  const manifestValidation =&lt;br&gt;
    validateSceneManifest(manifestCandidate);&lt;/p&gt;

&lt;p&gt;if (!manifestValidation.valid) {&lt;br&gt;
    return {&lt;br&gt;
      valid: false,&lt;br&gt;
      status: 400,&lt;br&gt;
      error: 'Invalid scene manifest',&lt;br&gt;
      details: manifestValidation.errors&lt;br&gt;
    };&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;room.sceneManifest =&lt;br&gt;
    manifestValidation.manifest;&lt;br&gt;
}&lt;br&gt;
Only after this check can the room continue through the lifecycle transition.&lt;br&gt;
The intended invariant is:&lt;br&gt;
invalid manifest&lt;br&gt;
      │&lt;br&gt;
      ▼&lt;br&gt;
HTTP 400&lt;br&gt;
      │&lt;br&gt;
      └── publication rejected&lt;br&gt;
while:&lt;br&gt;
valid manifest&lt;br&gt;
      │&lt;br&gt;
      ▼&lt;br&gt;
lifecycle validation&lt;br&gt;
      │&lt;br&gt;
      ▼&lt;br&gt;
draft → published&lt;br&gt;
This is stronger than validating only in the frontend. Frontend validation improves UX, but it cannot be authoritative because requests can be sent outside the official UI.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Persisting the Scene Contract
The room model now stores both the manifest version and manifest content:
sceneManifestVersion: {
type: String,
default: '1'
},&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;sceneManifest: {&lt;br&gt;
  type: mongoose.Schema.Types.Mixed,&lt;br&gt;
  default: null&lt;br&gt;
}&lt;br&gt;
The public room representation includes the scene configuration:&lt;br&gt;
function publicRoom(room) {&lt;br&gt;
  return {&lt;br&gt;
    id: room.roomId,&lt;br&gt;
    slug: room.slug,&lt;br&gt;
    name: room.name,&lt;br&gt;
    state: room.state,&lt;br&gt;
    accessPolicy: room.accessPolicy,&lt;br&gt;
    capacity: room.capacity,&lt;br&gt;
    stagePolicy: room.stagePolicy,&lt;br&gt;
    sceneManifestVersion:&lt;br&gt;
      room.sceneManifestVersion,&lt;br&gt;
    sceneManifest:&lt;br&gt;
      room.sceneManifest || null,&lt;br&gt;
    scheduledFor:&lt;br&gt;
      room.scheduledFor || null&lt;br&gt;
  };&lt;br&gt;
}&lt;br&gt;
This is the first version of the contract. It is not the final persistence design.&lt;br&gt;
The next implementation should separate the current manifest pointer from immutable historical versions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Testing the Contract
The validator includes tests for:&lt;/li&gt;
&lt;li&gt;A safe default manifest&lt;/li&gt;
&lt;li&gt;Versioned asset bundles&lt;/li&gt;
&lt;li&gt;Valid spawn points&lt;/li&gt;
&lt;li&gt;Valid interaction zones&lt;/li&gt;
&lt;li&gt;Portal destinations&lt;/li&gt;
&lt;li&gt;HTTPS links&lt;/li&gt;
&lt;li&gt;Unsupported schema versions&lt;/li&gt;
&lt;li&gt;Insecure asset URLs&lt;/li&gt;
&lt;li&gt;Malformed vectors&lt;/li&gt;
&lt;li&gt;Duplicate IDs&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unsupported capability requirements&lt;br&gt;
A simplified example:&lt;br&gt;
test(&lt;br&gt;
'rejects unsupported schema versions and insecure asset urls',&lt;br&gt;
() =&amp;gt; {&lt;br&gt;
const manifest = {&lt;br&gt;
  ...defaultSceneManifest(),&lt;/p&gt;

&lt;p&gt;schemaVersion: '2',&lt;/p&gt;

&lt;p&gt;assets: [{&lt;br&gt;
    id: 'plaza',&lt;br&gt;
    type: 'glb',&lt;br&gt;
    url: '&lt;a href="http://cdn.example.com/plaza.glb" rel="noopener noreferrer"&gt;http://cdn.example.com/plaza.glb&lt;/a&gt;'&lt;br&gt;
  }]&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;const result =&lt;br&gt;
  validateSceneManifest(manifest);&lt;/p&gt;

&lt;p&gt;expect(result.valid).toBe(false);&lt;/p&gt;

&lt;p&gt;expect(result.errors).toEqual(&lt;br&gt;
  expect.arrayContaining([&lt;br&gt;
    'schemaVersion must be 1',&lt;br&gt;
    'assets[0].url must use https'&lt;br&gt;
  ])&lt;br&gt;
);&lt;br&gt;
}&lt;br&gt;
);&lt;br&gt;
Another test verifies malformed vectors and duplicate IDs:&lt;br&gt;
test(&lt;br&gt;
'rejects malformed vectors, duplicate ids and unsupported capabilities',&lt;br&gt;
() =&amp;gt; {&lt;br&gt;
const manifest = {&lt;br&gt;
  ...defaultSceneManifest(),&lt;/p&gt;

&lt;p&gt;spawnPoints: [&lt;br&gt;
    {&lt;br&gt;
      id: 'same',&lt;br&gt;
      position: [0, 0]&lt;br&gt;
    },&lt;br&gt;
    {&lt;br&gt;
      id: 'same',&lt;br&gt;
      position: [0, 0, Number.NaN]&lt;br&gt;
    }&lt;br&gt;
  ],&lt;/p&gt;

&lt;p&gt;capabilities: ['telepathy']&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;const result =&lt;br&gt;
  validateSceneManifest(manifest);&lt;/p&gt;

&lt;p&gt;expect(result.valid).toBe(false);&lt;br&gt;
}&lt;br&gt;
);&lt;br&gt;
The merged changes passed:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Backend lint&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Backend unit tests&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Frontend unit tests&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Backend build&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Frontend build&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Security audit&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Continuous evidence checks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What We Verified in Production&lt;br&gt;
The current evidence boundary is important.&lt;br&gt;
We verified the following using a desktop browser against the production environment:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The metaverse page loaded&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authentication was active&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Neon Plaza loaded&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The session appeared online&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Controls were visible&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Emotes were available&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Chat was available&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The browser capability panel rendered&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Baseline browser checks passed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WebXR fallback messaging appeared&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The experience remained accessible without immersive WebXR&lt;br&gt;
We have not yet treated the following as fully verified:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Real-device mobile behavior&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Real-headset WebXR entry&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Real-headset WebXR exit&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Controller input across different headsets&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Long-running realtime reliability&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;High-concurrency room behavior&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CDN-backed versioned 3D asset loading&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manifest rollback in production&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Complete accessibility validation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A full LIFE pilot represented in the scene&lt;br&gt;
This distinction prevents roadmap intentions from being reported as completed features.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connecting the Metaverse to LIFE&lt;br&gt;
The MyZubster Metaverse should not become only a visual or social layer.&lt;br&gt;
Its more valuable role is to become an interface between:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Real-world pilots&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Technical evidence&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Community participation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Education&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Environmental indicators&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Replicable workflows&lt;br&gt;
The proposed LIFE-oriented flow is:&lt;br&gt;
Real-world pilot&lt;br&gt;
  ↓&lt;br&gt;
Observation or measurement&lt;br&gt;
  ↓&lt;br&gt;
Data provenance&lt;br&gt;
  ↓&lt;br&gt;
Evidence classification&lt;br&gt;
  ↓&lt;br&gt;
KPI / MRV calculation&lt;br&gt;
  ↓&lt;br&gt;
MyZubster project record&lt;br&gt;
  ↓&lt;br&gt;
Metaverse representation&lt;br&gt;
  ↓&lt;br&gt;
Mission or educational interaction&lt;br&gt;
  ↓&lt;br&gt;
Community feedback&lt;br&gt;
  ↓&lt;br&gt;
Improved pilot&lt;br&gt;
Example&lt;br&gt;
Imagine a LIFE-oriented water or circular-agriculture pilot.&lt;br&gt;
A participant enters Neon Plaza and approaches a portal labelled “Water Pilot”.&lt;br&gt;
The portal opens a dedicated scene. Inside that scene, the participant can inspect:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The pilot location at an appropriate privacy level&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The project methodology&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The instruments or sensors used&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The observation period&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Current evidence status&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Relevant KPIs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data-quality warnings&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Missing evidence&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Replication instructions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Public contribution tasks&lt;br&gt;
The scene might include a dashboard, but the dashboard must reference an authoritative evidence record.&lt;br&gt;
The scene itself is not evidence.&lt;br&gt;
3D visualization ≠ verified measurement&lt;br&gt;
simulation ≠ real-world result&lt;br&gt;
self-declaration ≠ independent validation&lt;br&gt;
project goal ≠ achieved impact&lt;br&gt;
The UI should make these boundaries visible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Evidence States for LIFE Scenes&lt;br&gt;
A useful next contract could attach evidence states to interactive objects:&lt;br&gt;
{&lt;br&gt;
"id": "water-savings-display",&lt;br&gt;
"type": "evidence-panel",&lt;br&gt;
"position": [4, 1.5, -2],&lt;br&gt;
"evidence": {&lt;br&gt;
"status": "measured",&lt;br&gt;
"source": "pilot-observation-2026-10",&lt;br&gt;
"methodologyVersion": "0.2",&lt;br&gt;
"verifiedBy": null,&lt;br&gt;
"independentVerification": false&lt;br&gt;
}&lt;br&gt;
}&lt;br&gt;
Possible evidence states could include:&lt;br&gt;
planned&lt;br&gt;
self-declared&lt;br&gt;
observed&lt;br&gt;
measured&lt;br&gt;
methodology-reviewed&lt;br&gt;
independently-verified&lt;br&gt;
disputed&lt;br&gt;
superseded&lt;br&gt;
This would allow the metaverse client to render different visual treatments:&lt;br&gt;
verified data        → solid green indicator&lt;br&gt;
measured, unverified → amber indicator&lt;br&gt;
self-declared        → blue information indicator&lt;br&gt;
simulation           → purple simulation indicator&lt;br&gt;
missing evidence     → grey warning indicator&lt;br&gt;
disputed             → red review indicator&lt;br&gt;
The goal is not to gamify uncertainty away. The goal is to make uncertainty understandable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What Is Still Missing&lt;br&gt;
The current implementation is a foundation, not a finished metaverse stack.&lt;br&gt;
22.1 Real mobile-device validation&lt;br&gt;
We still need structured testing on real devices:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Android Chrome&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Android Firefox&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;iOS Safari&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;iPadOS Safari&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Low-memory devices&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Slow or unstable networks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Small screens&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Touch-only navigation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Orientation changes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Background and resume behavior&lt;br&gt;
The UI claims that mobile browser access is supported as the intended baseline, but a complete real-device verification matrix is still required.&lt;br&gt;
22.2 Real WebXR headset testing&lt;br&gt;
We need physical tests for:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Entering immersive mode&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Exiting immersive mode&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Session interruption&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Controller input&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Focus loss&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Guardian or boundary changes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Headset sleep and resume&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unsupported-feature fallback&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Audio behavior&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Motion comfort&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance degradation&lt;br&gt;
Automated capability tests cannot replace physical-device validation.&lt;br&gt;
22.3 Immutable manifest history&lt;br&gt;
The current room stores its active manifest.&lt;br&gt;
The next persistence layer should add immutable versions:&lt;br&gt;
{&lt;br&gt;
roomId: 'room-123',&lt;br&gt;
version: '7',&lt;br&gt;
schemaVersion: '1',&lt;br&gt;
assetBundleVersion: '42',&lt;br&gt;
checksum: 'sha256:...',&lt;br&gt;
manifest: { /* immutable snapshot */ },&lt;br&gt;
createdBy: 'account-reference',&lt;br&gt;
createdAt: '2026-10-12T...'&lt;br&gt;
}&lt;br&gt;
A room should point to an active version rather than mutating history:&lt;br&gt;
VirtualRoom&lt;br&gt;
activeManifestVersion → 7&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;SceneManifestVersion&lt;br&gt;
  version 5&lt;br&gt;
  version 6&lt;br&gt;
  version 7&lt;br&gt;
22.4 Safe rollback&lt;br&gt;
Rollback should not destroy newer history.&lt;br&gt;
Instead, it should create a new version based on a previous valid snapshot:&lt;br&gt;
v5 → v6 → v7&lt;br&gt;
          │&lt;br&gt;
          └── rollback requested&lt;br&gt;
                    ↓&lt;br&gt;
                   v8&lt;br&gt;
       content derived from v6&lt;br&gt;
That preserves auditability.&lt;br&gt;
22.5 CDN and cache invalidation&lt;br&gt;
Versioned asset URLs should be immutable:&lt;br&gt;
/scenes/neon-plaza.42.glb&lt;br&gt;
/textures/plaza-floor.18.webp&lt;br&gt;
/audio/neon-ambient.7.ogg&lt;br&gt;
The manifest can change its pointer to a new asset version, while previous versions remain available for rollback.&lt;br&gt;
The CDN layer needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Immutable cache headers&lt;/li&gt;
&lt;li&gt;Content hashes&lt;/li&gt;
&lt;li&gt;CORS configuration&lt;/li&gt;
&lt;li&gt;Range requests where appropriate&lt;/li&gt;
&lt;li&gt;Compression strategy&lt;/li&gt;
&lt;li&gt;Cache invalidation rules&lt;/li&gt;
&lt;li&gt;Origin failure handling&lt;/li&gt;
&lt;li&gt;Asset availability checks
22.6 Graceful asset failure
A missing decorative object should not necessarily prevent the whole scene from loading.
The client needs failure classes:
critical asset failure
└── load fallback scene&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;important asset failure&lt;br&gt;
    └── continue with visible warning&lt;/p&gt;

&lt;p&gt;optional asset failure&lt;br&gt;
    └── omit asset and continue&lt;br&gt;
A future manifest entry could declare this explicitly:&lt;br&gt;
{&lt;br&gt;
  "id": "neon-plaza",&lt;br&gt;
  "type": "glb",&lt;br&gt;
  "url": "&lt;a href="https://cdn.myzubster.com/scenes/neon-plaza.42.glb" rel="noopener noreferrer"&gt;https://cdn.myzubster.com/scenes/neon-plaza.42.glb&lt;/a&gt;",&lt;br&gt;
  "criticality": "critical",&lt;br&gt;
  "fallbackAssetId": "basic-plaza"&lt;br&gt;
}&lt;br&gt;
22.7 Resource budgets&lt;br&gt;
Schema validity does not guarantee runtime safety.&lt;br&gt;
We still need budgets for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Asset file size&lt;/li&gt;
&lt;li&gt;Texture dimensions&lt;/li&gt;
&lt;li&gt;Total texture memory&lt;/li&gt;
&lt;li&gt;Polygon count&lt;/li&gt;
&lt;li&gt;Draw calls&lt;/li&gt;
&lt;li&gt;Animation complexity&lt;/li&gt;
&lt;li&gt;Audio memory&lt;/li&gt;
&lt;li&gt;Video resolution&lt;/li&gt;
&lt;li&gt;Concurrent downloads&lt;/li&gt;
&lt;li&gt;Scene initialization time
22.8 Realtime load testing
The realtime system needs controlled tests for:&lt;/li&gt;
&lt;li&gt;Concurrent joins&lt;/li&gt;
&lt;li&gt;Concurrent leaves&lt;/li&gt;
&lt;li&gt;Chat bursts&lt;/li&gt;
&lt;li&gt;Stage requests&lt;/li&gt;
&lt;li&gt;Participant moderation&lt;/li&gt;
&lt;li&gt;Token refresh&lt;/li&gt;
&lt;li&gt;Reconnect storms&lt;/li&gt;
&lt;li&gt;Server restart&lt;/li&gt;
&lt;li&gt;Database latency&lt;/li&gt;
&lt;li&gt;Redis degradation&lt;/li&gt;
&lt;li&gt;Network partitions
22.9 Accessibility
The browser-first experience needs continued testing for:&lt;/li&gt;
&lt;li&gt;Keyboard navigation&lt;/li&gt;
&lt;li&gt;Screen-reader labels&lt;/li&gt;
&lt;li&gt;Focus management&lt;/li&gt;
&lt;li&gt;Reduced motion&lt;/li&gt;
&lt;li&gt;Colour contrast&lt;/li&gt;
&lt;li&gt;Text scaling&lt;/li&gt;
&lt;li&gt;Captions&lt;/li&gt;
&lt;li&gt;Non-spatial alternatives&lt;/li&gt;
&lt;li&gt;Touch targets&lt;/li&gt;
&lt;li&gt;Cognitive load
An immersive view cannot be the only way to access important project information.
22.10 LIFE evidence integration
The first complete LIFE scene still needs:&lt;/li&gt;
&lt;li&gt;A selected pilot&lt;/li&gt;
&lt;li&gt;A defined methodology&lt;/li&gt;
&lt;li&gt;A provenance contract&lt;/li&gt;
&lt;li&gt;Evidence states&lt;/li&gt;
&lt;li&gt;KPI definitions&lt;/li&gt;
&lt;li&gt;MRV boundaries&lt;/li&gt;
&lt;li&gt;Privacy rules&lt;/li&gt;
&lt;li&gt;A public scene representation&lt;/li&gt;
&lt;li&gt;A contribution workflow&lt;/li&gt;
&lt;li&gt;A validation and review process&lt;/li&gt;
&lt;li&gt;Proposed Next Technical Milestones
The next roadmap can be structured into the following slices.
C2.1 — Immutable manifest versions
Create manifest version model
→ calculate checksum
→ store immutable snapshot
→ point room to active version
C2.2 — Manifest rollback
Select historical version
→ validate snapshot
→ create new version
→ update active pointer
→ preserve audit history
C2.3 — Asset pipeline
Upload
→ validate
→ scan
→ optimize
→ hash
→ version
→ CDN publish
→ manifest reference
C2.4 — Client scene loader
Fetch room
→ fetch manifest
→ check schema version
→ check capabilities
→ load critical assets
→ load optional assets
→ expose failures
→ enter scene
C2.5 — LIFE pilot scene
Pilot record
→ evidence contract
→ KPI/MRV mapping
→ manifest generation
→ scene visualization
→ public test&lt;/li&gt;
&lt;li&gt;Links to the Work
Main repository&lt;/li&gt;
&lt;li&gt;MyZubster-Ecosystem/myzubster
Browser and WebXR fallback&lt;/li&gt;
&lt;li&gt;PR #1597 — Non-blocking WebXR capability fallback
Versioned scene manifest&lt;/li&gt;
&lt;li&gt;PR #1601 — Scene manifest validation and publication gate
Live experience&lt;/li&gt;
&lt;li&gt;MyZubster Metaverse&lt;/li&gt;
&lt;li&gt;MyZubster&lt;/li&gt;
&lt;li&gt;LIFE Pilot&lt;/li&gt;
&lt;li&gt;How Developers Can Contribute
The most useful contributions right now include:&lt;/li&gt;
&lt;li&gt;Android and iOS browser test reports&lt;/li&gt;
&lt;li&gt;Real-headset WebXR test reports&lt;/li&gt;
&lt;li&gt;Scene-manifest reviews&lt;/li&gt;
&lt;li&gt;glTF and GLB validation&lt;/li&gt;
&lt;li&gt;3D performance budgets&lt;/li&gt;
&lt;li&gt;Asset fallback design&lt;/li&gt;
&lt;li&gt;Accessibility reviews&lt;/li&gt;
&lt;li&gt;Realtime load-test scenarios&lt;/li&gt;
&lt;li&gt;Security reviews&lt;/li&gt;
&lt;li&gt;CDN architecture&lt;/li&gt;
&lt;li&gt;Manifest version-history design&lt;/li&gt;
&lt;li&gt;Rollback implementation&lt;/li&gt;
&lt;li&gt;LIFE evidence modelling&lt;/li&gt;
&lt;li&gt;KPI and MRV data contracts&lt;/li&gt;
&lt;li&gt;Technical documentation
A useful contribution should include reproducible evidence:
environment
browser or device
exact version
steps performed
expected result
observed result
screenshots or logs when safe
related issue or pull request
Please do not publish:&lt;/li&gt;
&lt;li&gt;Passwords&lt;/li&gt;
&lt;li&gt;Authentication tokens&lt;/li&gt;
&lt;li&gt;Private wallet keys&lt;/li&gt;
&lt;li&gt;Seed phrases&lt;/li&gt;
&lt;li&gt;Private participant information&lt;/li&gt;
&lt;li&gt;Precise sensitive locations&lt;/li&gt;
&lt;li&gt;Unredacted personal data
Conclusion
The MyZubster Metaverse now has two important foundations:&lt;/li&gt;
&lt;li&gt;A browser-first runtime that treats WebXR as progressive enhancement&lt;/li&gt;
&lt;li&gt;A server-side scene contract that blocks invalid manifests from publication
The completed work is publicly traceable through:&lt;/li&gt;
&lt;li&gt;PR #1597&lt;/li&gt;
&lt;li&gt;PR #1601
The next challenge is not simply adding more 3D content.
It is building a reliable chain from versioned assets and authoritative sessions to transparent, evidence-aware virtual experiences:
code
→ validated contract
→ versioned scene
→ reliable session
→ real-world pilot
→ evidence
→ understandable visualization
→ community contribution
That is the direction of the MyZubster Metaverse: an open, browser-first environment where virtual experiences connect to documented projects, reproducible evidence, and real-world collaboration.
Status verified: October 12, 2026.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>metaverse</category>
      <category>browser</category>
      <category>sever</category>
      <category>rooms</category>
    </item>
    <item>
      <title>Bringing MyZubster to the Tor Network: Building an Onion with Docker, React and Node.js</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Sat, 10 Oct 2026 04:12:57 +0000</pubDate>
      <link>https://dev.to/danielioni/bringing-myzubster-to-the-tor-network-building-an-onion-with-docker-react-and-nodejs-2m2p</link>
      <guid>https://dev.to/danielioni/bringing-myzubster-to-the-tor-network-building-an-onion-with-docker-react-and-nodejs-2m2p</guid>
      <description>&lt;h1&gt;
  
  
  Bringing MyZubster to the Tor Network: Building an Onion Service with Docker, React and Node.js
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Building a privacy-conscious access layer for an open-source ecosystem — with real tests, reproducible bugs, and contributions from the community.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The next chapter of &lt;strong&gt;MyZubster&lt;/strong&gt; is taking shape.&lt;/p&gt;

&lt;p&gt;We're working toward making MyZubster accessible through the &lt;strong&gt;Tor network using a &lt;code&gt;.onion&lt;/code&gt; address&lt;/strong&gt;, alongside our traditional website.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;And we're building it openly, with community contributors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet the contributors
&lt;/h2&gt;

&lt;p&gt;Two people deserve particular recognition for the work we're currently reviewing and validating.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mohammed Wasim Khan — GitHub: &lt;a href="https://github.com/wasim-builds" rel="noopener noreferrer"&gt;@wasim-builds&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Wasim has been working on a pull request that brings together improvements to Docker infrastructure, backend services, frontend configuration, and the Tor integration.&lt;/p&gt;

&lt;p&gt;His current contribution is available here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1582" rel="noopener noreferrer"&gt;MyZubster PR #1582 — Wasim's contribution&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nicola — N4K48&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nicola's independent pilot and earlier contribution form an important part of the work that is now being reviewed and extended.&lt;/p&gt;

&lt;p&gt;You can explore the original contribution here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1457" rel="noopener noreferrer"&gt;MyZubster PR #1457 — Nicola / N4K48&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are we building?
&lt;/h2&gt;

&lt;p&gt;The objective is to provide a second access path to MyZubster through a Tor Onion Service.&lt;/p&gt;

&lt;p&gt;The proposed architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The concept is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Onion Service is an alternative entry point, not a replacement for the application.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The backend and database do not need to expose public ports just because users can reach the frontend over Tor.&lt;/p&gt;

&lt;p&gt;That separation is an important part of the deployment design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technology stack
&lt;/h2&gt;

&lt;p&gt;The components under test include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker and Docker Compose for service isolation.&lt;/li&gt;
&lt;li&gt;Tor on Debian Bookworm.&lt;/li&gt;
&lt;li&gt;React for the frontend.&lt;/li&gt;
&lt;li&gt;Nginx for serving the application and proxying API requests.&lt;/li&gt;
&lt;li&gt;Node.js and Express for backend services.&lt;/li&gt;
&lt;li&gt;MongoDB for application data.&lt;/li&gt;
&lt;li&gt;JWT middleware for authentication and authorization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Tor container runs using the dedicated &lt;code&gt;debian-tor&lt;/code&gt; user.&lt;/p&gt;

&lt;p&gt;The Onion Service identity is designed to persist through a Docker volume, and telemetry is disabled by default.&lt;/p&gt;

&lt;p&gt;These are useful foundations, but secure and reliable operation still requires runtime validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we tested on an isolated VPS
&lt;/h2&gt;

&lt;p&gt;We created a dedicated Docker test environment rather than deploying the pull request directly into production.&lt;/p&gt;

&lt;p&gt;The environment used separate containers, an internal Docker network, and a dedicated MongoDB volume.&lt;/p&gt;

&lt;p&gt;No application ports were published to the VPS host as part of these tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Backend and MongoDB
&lt;/h3&gt;

&lt;p&gt;The backend image compiled successfully.&lt;/p&gt;

&lt;p&gt;However, the original backend initially failed during startup because of an incorrect middleware import:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cannot find module '../../../src/middleware/auth'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The affected file was:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;backend/src/routes/metaverse.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We tested a one-line local correction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-const { authenticate, optionalAuthenticate } = require('../../../src/middleware/auth');
&lt;/span&gt;&lt;span class="gi"&gt;+const { authenticate, optionalAuthenticate } = require('../middleware/auth');
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After rebuilding with that local change, the backend started and connected to MongoDB.&lt;/p&gt;

&lt;p&gt;The health endpoint returned HTTP 200.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; this fix was tested locally; the pull request itself still needs the correction.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Ledger and Economics APIs
&lt;/h3&gt;

&lt;p&gt;We also exercised the application's experimental API endpoints.&lt;/p&gt;

&lt;p&gt;Results included:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /api/ledger&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP 200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /api/ledger/balances&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP 200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST /api/economics/simulate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP 200&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;These tests demonstrate working API responses in the isolated environment, not production financial operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Authentication and authorization
&lt;/h3&gt;

&lt;p&gt;We ran seven targeted checks across Chat, Notifications, and an administrative metrics endpoint.&lt;/p&gt;

&lt;p&gt;All seven passed.&lt;/p&gt;

&lt;p&gt;The tests included requests without tokens, invalid bearer tokens, valid synthetic user tokens, and valid synthetic administrator tokens.&lt;/p&gt;

&lt;p&gt;The administrative endpoint returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No token       -&amp;gt; HTTP 401
Standard user  -&amp;gt; HTTP 403
Administrator  -&amp;gt; HTTP 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us encouraging evidence about the specific middleware paths we tested, although it is not a complete security audit.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. React and Nginx
&lt;/h3&gt;

&lt;p&gt;The React production build completed successfully.&lt;/p&gt;

&lt;p&gt;The frontend Docker image also built, and Nginx validated on the isolated Docker network.&lt;/p&gt;

&lt;p&gt;We confirmed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /                       -&amp;gt; 200
GET /synthetic-spa-route    -&amp;gt; 200
GET /api/ledger             -&amp;gt; 200
GET /api/ledger/balances    -&amp;gt; 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last two requests passed through the Nginx reverse proxy to the backend.&lt;/p&gt;

&lt;p&gt;We also identified a build-hardening improvement.&lt;/p&gt;

&lt;p&gt;The frontend Dockerfile currently includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Build skipped for now"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That can conceal future React compilation failures.&lt;/p&gt;

&lt;p&gt;For a deployment image, we recommend making the build fail when compilation fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The most interesting challenge: Tor and Docker DNS
&lt;/h2&gt;

&lt;p&gt;The Onion Service component introduced a particularly useful debugging exercise.&lt;/p&gt;

&lt;p&gt;The original entrypoint generated a Tor configuration with a destination similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HiddenServicePort 80 frontend:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;frontend&lt;/code&gt; is the Docker Compose service name.&lt;/p&gt;

&lt;p&gt;Docker containers can resolve that hostname through their internal DNS service.&lt;/p&gt;

&lt;p&gt;However, when we tested this configuration with Tor 0.4.9.11, the validation failed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unparseable address in hidden service port configuration.
Failed to parse/validate config.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a numeric IP address passed Tor's configuration validation.&lt;/p&gt;

&lt;p&gt;We therefore tested a local candidate approach: resolve the Docker service name before generating &lt;code&gt;torrc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TARGET_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ONION_TARGET_HOST&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;frontend&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;TARGET_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;getent ahostsv4 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARGET_HOST&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR == 1 {print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then generate the destination using the resolved address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HiddenServicePort 80 &amp;lt;resolved-container-ip&amp;gt;:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our isolated environment, Docker DNS resolved the frontend to an internal IP.&lt;/p&gt;

&lt;p&gt;We confirmed that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Tor container could resolve the frontend hostname.&lt;/li&gt;
&lt;li&gt;The frontend responded with HTTP 200 from that Docker network.&lt;/li&gt;
&lt;li&gt;The configuration using the resolved IP passed &lt;code&gt;tor --verify-config&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The modified Onion image built successfully.&lt;/li&gt;
&lt;li&gt;The modified entrypoint generated a configuration accepted by Tor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  But there is a catch
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Docker container IP addresses are not necessarily stable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the frontend is recreated, its address may change.&lt;/p&gt;

&lt;p&gt;Resolving the address only once at startup may therefore leave Tor pointing at an outdated destination.&lt;/p&gt;

&lt;p&gt;This is why we're treating the change as a tested candidate fix, not a final production solution.&lt;/p&gt;

&lt;p&gt;A robust design needs to account for startup readiness, service recreation, address changes, and safe reconfiguration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy and operational considerations
&lt;/h2&gt;

&lt;p&gt;Supporting &lt;code&gt;.onion&lt;/code&gt; access does not automatically make the entire application anonymous.&lt;/p&gt;

&lt;p&gt;Authentication, application logs, external resources, browser behavior, and telemetry all matter.&lt;/p&gt;

&lt;p&gt;Our current approach emphasizes separation of services, protection of Onion identity material, and explicit privacy boundaries.&lt;/p&gt;

&lt;p&gt;Telemetry is disabled by default.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is still missing?
&lt;/h2&gt;

&lt;p&gt;Before we can describe the Onion deployment as ready for public use, we need to complete several tasks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Integrate the backend authentication import correction.&lt;/li&gt;
&lt;li&gt;Implement resilient destination resolution for Tor.&lt;/li&gt;
&lt;li&gt;Handle frontend recreation and Docker DNS readiness.&lt;/li&gt;
&lt;li&gt;Improve Tor health and connectivity checks.&lt;/li&gt;
&lt;li&gt;Make frontend Docker builds fail reliably on compilation errors.&lt;/li&gt;
&lt;li&gt;Validate the actual Onion Service in a controlled Tor-network test.&lt;/li&gt;
&lt;li&gt;Verify browser functionality and end-to-end behavior.&lt;/li&gt;
&lt;li&gt;Review application privacy boundaries and security before public deployment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;We have not yet launched or independently verified a publicly reachable MyZubster &lt;code&gt;.onion&lt;/code&gt; service through these tests.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The work is in active development and review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;For us, open source means more than publishing code.&lt;/p&gt;

&lt;p&gt;It means giving contributors meaningful work, recognizing their contributions, reproducing problems independently, and sharing evidence even when something doesn't work yet.&lt;/p&gt;

&lt;p&gt;Nicola's N4K48 work and Wasim's ongoing contribution demonstrate how independent efforts can be reviewed and developed toward a shared technical goal.&lt;/p&gt;

&lt;p&gt;And the Tor integration raises questions that other Docker and privacy-focused developers may have encountered before.&lt;/p&gt;

&lt;h2&gt;
  
  
  We would love feedback from the community
&lt;/h2&gt;

&lt;p&gt;If you have experience with &lt;strong&gt;Tor Onion Services, Docker DNS, container lifecycle management, Nginx, or privacy-conscious infrastructure&lt;/strong&gt;, we'd especially appreciate your input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How would you handle a Tor Onion Service backend whose Docker container IP can change after recreation?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Would you use a local forwarding proxy, a service-discovery strategy, controlled Tor configuration reloads, or something else?&lt;/p&gt;

&lt;p&gt;We're interested in practical, reproducible approaches.&lt;/p&gt;

&lt;h3&gt;
  
  
  Explore the project and contributors
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;MyZubster repository:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster" rel="noopener noreferrer"&gt;https://github.com/MyZubster-Ecosystem/myzubster&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MyZubster website:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.myzubster.com/" rel="noopener noreferrer"&gt;https://www.myzubster.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wasim — GitHub profile:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/wasim-builds" rel="noopener noreferrer"&gt;https://github.com/wasim-builds&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wasim — current pull request #1582:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1582" rel="noopener noreferrer"&gt;https://github.com/MyZubster-Ecosystem/myzubster/pull/1582&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nicola / N4K48 — original contribution #1457:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/pull/1457" rel="noopener noreferrer"&gt;https://github.com/MyZubster-Ecosystem/myzubster/pull/1457&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A sincere thank you to &lt;strong&gt;Wasim and Nicola&lt;/strong&gt; for contributing their work to the MyZubster ecosystem.&lt;/p&gt;

&lt;p&gt;We're building this step by step, testing carefully, and keeping the process open for people who want to help.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The next milestone isn't simply having an Onion address. It's making that address reliable, secure, maintainable, and useful.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And that's something worth building together.&lt;/p&gt;

</description>
      <category>tor</category>
      <category>docker</category>
      <category>onion</category>
      <category>myzubster</category>
    </item>
    <item>
      <title>Debugging ZORGAX: How We Traced an AI Agent Tool-Calling Loop from Open WebUI to Ollama</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Fri, 09 Oct 2026 06:24:19 +0000</pubDate>
      <link>https://dev.to/danielioni/debugging-zorgax-how-we-traced-an-ai-agent-tool-calling-loop-from-open-webui-to-ollama-2mhp</link>
      <guid>https://dev.to/danielioni/debugging-zorgax-how-we-traced-an-ai-agent-tool-calling-loop-from-open-webui-to-ollama-2mhp</guid>
      <description>&lt;h1&gt;
  
  
  Debugging ZORGAX: How We Traced an AI Agent Tool-Calling Loop from Open WebUI to Ollama
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;An evidence-first debugging story, with a reproducible test and an open invitation to contributors.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Building an AI agent is not just about making a language model call tools. It is also about ensuring the model knows when to stop.&lt;/p&gt;

&lt;p&gt;While developing &lt;strong&gt;ZORGAX&lt;/strong&gt;, a local DevOps AI agent within the MyZubster ecosystem, I encountered a tool-calling problem: when asked which automations were active, ZORGAX repeatedly invoked &lt;code&gt;list_automations&lt;/code&gt; even after the tool returned a successful empty result.&lt;/p&gt;

&lt;p&gt;The expected behavior was simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call &lt;code&gt;list_automations&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Receive &lt;code&gt;{"automations":[],"total":0}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Tell the user that no automations are active.&lt;/li&gt;
&lt;li&gt;Stop.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead, the Open WebUI integration sometimes repeated the tool call until interrupted.&lt;/p&gt;

&lt;p&gt;We investigated the problem using a custom ZORGAX model derived from Qwen2.5 3B, Ollama, Open WebUI, Docker, and a temporary HTTP diagnostic proxy.&lt;/p&gt;

&lt;p&gt;Here is what we discovered.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The first clue: direct Ollama worked
&lt;/h2&gt;

&lt;p&gt;Our first experiment bypassed Open WebUI.&lt;/p&gt;

&lt;p&gt;We sent a synthetic conversation to Ollama's &lt;code&gt;/api/chat&lt;/code&gt; endpoint, including an assistant tool call and a matching tool result.&lt;/p&gt;

&lt;p&gt;The result was exactly what we wanted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Content: Non ci sono automazioni attive al momento.
Tool calls: []
Done: True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ZORGAX understood the empty result and produced a final answer without calling the tool again.&lt;/p&gt;

&lt;p&gt;This was useful, but it did not prove Open WebUI was at fault. The direct test used a simplified system prompt and tool schema. We needed to compare the actual integration path.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Following the tool result through Open WebUI
&lt;/h2&gt;

&lt;p&gt;A commenter on our earlier Coder Legion article suggested a more precise investigation: capture the request immediately after the first &lt;code&gt;list_automations&lt;/code&gt; result and compare it with a working Ollama control.&lt;/p&gt;

&lt;p&gt;That changed the direction of our debugging.&lt;/p&gt;

&lt;p&gt;We inspected the installed Open WebUI source and identified its tool-call continuation flow.&lt;/p&gt;

&lt;p&gt;The middleware transforms tool execution output back into conversation messages, including the assistant's &lt;code&gt;tool_calls&lt;/code&gt; and a matching &lt;code&gt;role: tool&lt;/code&gt; message.&lt;/p&gt;

&lt;p&gt;Rather than relying on source inspection alone, we extracted and exercised the actual &lt;code&gt;convert_output_to_messages()&lt;/code&gt; function using a harmless fixture.&lt;/p&gt;

&lt;p&gt;The test produced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tool_calls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"zorgax-test-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list_automations"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{}"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tool_call_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"zorgax-test-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;automations&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:[],&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;total&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:0}"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The conversion test passed.&lt;/p&gt;

&lt;p&gt;That ruled out a basic failure of this function for our isolated empty-result case. It did not establish that every subsequent network request would preserve the same structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Adding a safety limit
&lt;/h2&gt;

&lt;p&gt;We also found that the investigated Open WebUI build had a default limit of 256 tool-call iterations.&lt;/p&gt;

&lt;p&gt;For debugging, that was far more than we needed.&lt;/p&gt;

&lt;p&gt;In our test setup, we configured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CHAT_RESPONSE_MAX_TOOL_CALL_ITERATIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After applying the setting, an isolated Open WebUI experiment reproduced the repeated calls and stopped with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tool-call iteration limit reached (3)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was a safety improvement, not a root-cause fix. A single iteration can contain multiple tool calls, so the limit should not be interpreted as an exact cap on individual invocations.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Building a disposable diagnostic environment
&lt;/h2&gt;

&lt;p&gt;We created a separate Open WebUI instance rather than modifying the main MyZubster interface.&lt;/p&gt;

&lt;p&gt;We then added a harmless custom tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
title: ZORGAX Automation Test
description: Safe automation-listing test fixture.
version: 1.0.0
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_automations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return a synthetic list of active automations.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;automations&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function never reads real automations, touches a database, or performs an administrative action.&lt;/p&gt;

&lt;p&gt;That isolation matters. Debugging an agent with operational tools should not require putting production infrastructure at risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Capturing the actual Ollama requests
&lt;/h2&gt;

&lt;p&gt;Next, we introduced a temporary HTTP proxy between the diagnostic Open WebUI instance and Ollama.&lt;/p&gt;

&lt;p&gt;The proxy forwarded requests while recording only structural metadata: model name, message roles, tool-call IDs, tool names, and whether the empty-result fixture was present.&lt;/p&gt;

&lt;p&gt;We deliberately avoided storing complete conversation prompts and authentication data.&lt;/p&gt;

&lt;p&gt;The first interesting observation was that one captured request exposed approximately &lt;strong&gt;35 built-in tools&lt;/strong&gt; to the model.&lt;/p&gt;

&lt;p&gt;That was substantially different from the successful direct Ollama test, which exposed only &lt;code&gt;list_automations&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, this alone did not prove the number of tools caused the loop.&lt;/p&gt;

&lt;p&gt;We needed a narrower experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The successful end-to-end experiment
&lt;/h2&gt;

&lt;p&gt;We configured &lt;strong&gt;ZORGAX Diagnostic&lt;/strong&gt; to use a single custom tool and disabled the unrelated built-in capabilities.&lt;/p&gt;

&lt;p&gt;Then we asked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Controlla quali automazioni sono attive usando &lt;code&gt;list_automations&lt;/code&gt;. Se non ce ne sono, dimmelo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ZORGAX invoked the simulated tool and replied that no automations were active.&lt;/p&gt;

&lt;p&gt;More importantly, the proxy captured the continuation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request 7
  Model: zorgax:latest
  Tools: [list_automations]
  Messages:
    user

Request 8
  Model: zorgax:latest
  Tools: [list_automations]
  Messages:
    user
    assistant -&amp;gt; list_automations
    tool -&amp;gt; empty result present
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assistant's call identifier and the tool result were preserved, and the empty result reached the next request destined for Ollama.&lt;/p&gt;

&lt;p&gt;For this controlled configuration, the full interaction worked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That is our most important verified finding: Open WebUI can carry a successful empty tool result through to Ollama and obtain a final response from ZORGAX.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. What we have not proven
&lt;/h2&gt;

&lt;p&gt;It would be tempting to conclude that exposing too many tools caused the original loop.&lt;/p&gt;

&lt;p&gt;But that would be premature.&lt;/p&gt;

&lt;p&gt;The successful and unsuccessful experiments did not hold every variable constant. Tool availability was different, and other factors — including prompt instructions, function schemas, model parameters, and sampling behavior — might also affect the result.&lt;/p&gt;

&lt;p&gt;We therefore have a strong debugging lead, not a definitive explanation.&lt;/p&gt;

&lt;p&gt;Our next objective is a controlled A/B comparison using identical model versions, prompts, tool schemas, and generation options while changing one variable at a time.&lt;/p&gt;

&lt;p&gt;We also want regression tests that distinguish three important cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A tool successfully returns an empty result.&lt;/li&gt;
&lt;li&gt;A tool fails with a transport or execution error.&lt;/li&gt;
&lt;li&gt;A model generates invalid tool arguments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An empty result is valid information. An error is not the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Opening the investigation to the community
&lt;/h2&gt;

&lt;p&gt;We have now published the investigation in the public MyZubster GitHub repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical reproduction guide:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/blob/main/docs/zorgax/TOOL-CALLING-INVESTIGATION-2026-10-09.md" rel="noopener noreferrer"&gt;https://github.com/MyZubster-Ecosystem/myzubster/blob/main/docs/zorgax/TOOL-CALLING-INVESTIGATION-2026-10-09.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contributor investigation issue:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/issues/1577" rel="noopener noreferrer"&gt;https://github.com/MyZubster-Ecosystem/myzubster/issues/1577&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contribution guidelines:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster/blob/main/CONTRIBUTING.md" rel="noopener noreferrer"&gt;https://github.com/MyZubster-Ecosystem/myzubster/blob/main/CONTRIBUTING.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Contributors can reproduce the problem on their own machines, improve the fixtures, compare sanitized HTTP payloads, add regression tests, or propose changes to observability and error handling.&lt;/p&gt;

&lt;p&gt;No one needs access to the MyZubster VPS or any production credentials.&lt;/p&gt;

&lt;p&gt;We welcome small, focused contributions backed by clear evidence.&lt;/p&gt;
&lt;h2&gt;
  
  
  9. The bigger lesson
&lt;/h2&gt;

&lt;p&gt;A tool-calling loop is not always something you can fix by adding another instruction such as “do not repeat the call.”&lt;/p&gt;

&lt;p&gt;The more reliable approach is to inspect the complete sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User request
    ↓
Assistant tool call
    ↓
Tool execution
    ↓
Tool result
    ↓
Follow-up request to the model
    ↓
Final response or another tool call
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each boundary needs to preserve the right context.&lt;/p&gt;

&lt;p&gt;This investigation taught us to separate three questions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Did the tool execute?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Did its result reach the next model request?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Did the model make the right decision after receiving it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Only by testing those questions independently can we narrow down the cause of an agent loop without blaming the wrong component.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next for ZORGAX?
&lt;/h2&gt;

&lt;p&gt;ZORGAX is intended to become a dependable local DevOps assistant for monitoring, diagnostics, and carefully authorized infrastructure operations.&lt;/p&gt;

&lt;p&gt;The successful single-tool experiment is an important milestone, but the original loop investigation remains open.&lt;/p&gt;

&lt;p&gt;We are continuing that work in public so others can reproduce the evidence, challenge our assumptions, and help build a more reliable agent.&lt;/p&gt;

&lt;p&gt;If you work with Ollama, Open WebUI, Qwen, or local AI agents, you're welcome to join the investigation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build in public. Test with evidence. Keep humans in control.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>zorgax</category>
      <category>ollama</category>
      <category>ai</category>
      <category>webui</category>
    </item>
    <item>
      <title>Building ZORGAX: From a Local AI Model to a Server Operations Agent for MyZubster</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Fri, 09 Oct 2026 03:15:40 +0000</pubDate>
      <link>https://dev.to/danielioni/building-zorgax-from-a-local-ai-model-to-a-server-operations-agent-for-myzubster-5336</link>
      <guid>https://dev.to/danielioni/building-zorgax-from-a-local-ai-model-to-a-server-operations-agent-for-myzubster-5336</guid>
      <description>&lt;h1&gt;
  
  
  Building ZORGAX: From a Local AI Model to a Server Operations Agent for MyZubster
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Connecting Ollama, Docker, Open WebUI and a custom AI assistant — and learning why reliable tool calling matters.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We're working on a new capability inside the &lt;strong&gt;MyZubster ecosystem&lt;/strong&gt;: ZORGAX, an AI assistant intended to help observe, diagnose and eventually manage technical infrastructure.&lt;/p&gt;

&lt;p&gt;Our goal isn't simply to create another chatbot.&lt;/p&gt;

&lt;p&gt;We want an AI agent that can understand what's happening across our servers, services and software projects — while operating under explicit permissions and human oversight.&lt;/p&gt;

&lt;p&gt;Here's what we've built and tested so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Our current infrastructure
&lt;/h2&gt;

&lt;p&gt;The pilot environment includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ubuntu 24.04 LTS&lt;/strong&gt; running on our VPS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker and Docker Compose&lt;/strong&gt; for isolated services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ollama&lt;/strong&gt; for local LLM inference&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qwen 2.5 3B&lt;/strong&gt; as the foundation model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ZORGAX&lt;/strong&gt;, our custom Ollama model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open WebUI&lt;/strong&gt; as the conversational interface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;N4K48&lt;/strong&gt;, our independent AI integration pilot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qdrant&lt;/strong&gt; for vector infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture allows our AI services to communicate internally without exposing the Ollama API directly to the public internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Connecting Docker applications to Ollama
&lt;/h2&gt;

&lt;p&gt;One of our first challenges involved networking.&lt;/p&gt;

&lt;p&gt;Ollama was available at:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://172.22.0.1:11434&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But Open WebUI was configured to use:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://host.docker.internal:11434&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Inside the container, that hostname resolved to &lt;code&gt;172.17.0.1&lt;/code&gt;, where requests timed out.&lt;/p&gt;

&lt;p&gt;We corrected the Open WebUI connection to the reachable Ollama endpoint and verified it with an API request.&lt;/p&gt;

&lt;p&gt;The result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP: 200

Models:
- nomic-embed-text:latest
- zorgax:latest
- qwen2.5:3b

CONNECTION: OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also introduced a systemd socket proxy to make Ollama accessible through the VPS loopback interface:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;127.0.0.1:11434&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This preserved the existing Ollama listener used by Docker.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Testing actual model inference
&lt;/h2&gt;

&lt;p&gt;Connectivity alone isn't enough.&lt;/p&gt;

&lt;p&gt;We tested text generation directly from the Open WebUI container using Ollama's &lt;code&gt;/api/generate&lt;/code&gt; endpoint.&lt;/p&gt;

&lt;p&gt;The result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RESPONSE: OLLAMA OK
DONE: True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Open WebUI container subsequently passed its healthcheck:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;running / healthy
HTTP 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then connected through an SSH tunnel and successfully interacted with ZORGAX in the browser.&lt;/p&gt;

&lt;p&gt;At this stage, we had working infrastructure and a functioning conversational AI.&lt;/p&gt;

&lt;p&gt;But the next challenge was more interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The tool-calling loop problem
&lt;/h2&gt;

&lt;p&gt;We asked ZORGAX to identify which tools it could actually use.&lt;/p&gt;

&lt;p&gt;It invoked a function called:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;list_automations&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The tool returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"automations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of terminating the process and reporting that no active automations were found, ZORGAX repeatedly called the same function.&lt;/p&gt;

&lt;p&gt;We observed one sequence reaching 28 calls and another reaching 16 calls.&lt;/p&gt;

&lt;p&gt;This exposed an important engineering problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An AI agent must not confuse the ability to call a tool with the ability to manage a workflow reliably.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Repeated tool calls are inefficient. More importantly, they could become dangerous if the tools eventually acquire permissions to modify real infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Isolating the issue
&lt;/h2&gt;

&lt;p&gt;We tested ZORGAX directly through Ollama, without Open WebUI.&lt;/p&gt;

&lt;p&gt;First, we confirmed that it could answer normally without tools.&lt;/p&gt;

&lt;p&gt;Then we simulated a complete tool interaction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user asked how many active automations existed.&lt;/li&gt;
&lt;li&gt;ZORGAX requested &lt;code&gt;list_automations&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The test supplied an empty result.&lt;/li&gt;
&lt;li&gt;ZORGAX responded that no active automations were available.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There was no repeated tool call in that controlled test.&lt;/p&gt;

&lt;p&gt;This suggests that the problem requires further investigation into the Open WebUI tool execution path, model configuration and calling behavior.&lt;/p&gt;

&lt;p&gt;It does not yet establish a single root cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. What comes next: ZORGAX as a server operations agent
&lt;/h2&gt;

&lt;p&gt;Our next objective is to move beyond conversation and build a carefully controlled operational assistant.&lt;/p&gt;

&lt;p&gt;The first planned capability is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;myzubster_server_status&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This will be a read-only tool for collecting selected infrastructure health information.&lt;/p&gt;

&lt;p&gt;The long-term roadmap includes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1 — Observation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Read-only server metrics, Docker container health, systemd service status and selected application healthchecks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2 — Diagnosis&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Analyze bounded logs, identify anomalies and suggest possible explanations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 3 — Project intelligence&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Review repository status, test results and project diagnostics without automatically merging or deploying changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 4 — Authorized operations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Execute predefined maintenance actions only with explicit approval, restricted permissions and audit logging.&lt;/p&gt;

&lt;p&gt;We are intentionally not giving the AI unrestricted root access or direct control over the Docker socket.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. The main lesson
&lt;/h2&gt;

&lt;p&gt;Building an AI assistant is relatively straightforward.&lt;/p&gt;

&lt;p&gt;Building an AI agent that can interact reliably with real systems is a different engineering challenge.&lt;/p&gt;

&lt;p&gt;The important questions become:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can it distinguish facts from assumptions?&lt;/li&gt;
&lt;li&gt;Can it interpret tool results correctly?&lt;/li&gt;
&lt;li&gt;Can it avoid duplicate or unnecessary operations?&lt;/li&gt;
&lt;li&gt;Can it stop when an operation is complete?&lt;/li&gt;
&lt;li&gt;Can it operate with least-privilege permissions?&lt;/li&gt;
&lt;li&gt;Can every consequential action be audited?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For MyZubster, these are not optional details. They are foundational requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;ZORGAX is now functioning as a conversational assistant through Ollama and Open WebUI.&lt;/p&gt;

&lt;p&gt;We have validated container connectivity, model inference and a controlled example of tool calling.&lt;/p&gt;

&lt;p&gt;The next stage is improving tool execution reliability and introducing a read-only infrastructure monitoring interface.&lt;/p&gt;

&lt;p&gt;Our direction is clear:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;From a local AI model to a permission-controlled infrastructure assistant — one verified capability at a time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is an ongoing development experiment, not a production-ready autonomous system.&lt;/p&gt;

&lt;p&gt;More engineering notes will follow as we test each component.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built as part of the MyZubster ecosystem and the N4K48 independent AI integration pilot.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>myzubster</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>From Pull Requests to Reproducible Interoperability: Building an Evidence-First Contributor Network</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Thu, 08 Oct 2026 18:15:35 +0000</pubDate>
      <link>https://dev.to/danielioni/from-pull-requests-to-reproducible-interoperability-building-an-evidence-first-contributor-network-69o</link>
      <guid>https://dev.to/danielioni/from-pull-requests-to-reproducible-interoperability-building-an-evidence-first-contributor-network-69o</guid>
      <description>&lt;h1&gt;
  
  
  From Pull Requests to Reproducible Interoperability: Building an Evidence-First Contributor Network
&lt;/h1&gt;

&lt;p&gt;Open source collaboration becomes much more useful when contribution history is not just a list of usernames and merged pull requests, but a network of &lt;strong&gt;reproducible technical evidence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://github.com/MyZubster-Ecosystem/myzubster" rel="noopener noreferrer"&gt;MyZubster&lt;/a&gt;, we have been working on a contributor model where public work can be connected to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exact pull requests;&lt;/li&gt;
&lt;li&gt;canonical merge commits;&lt;/li&gt;
&lt;li&gt;independent test checkpoints;&lt;/li&gt;
&lt;li&gt;external contributor-owned repositories;&lt;/li&gt;
&lt;li&gt;bounded interoperability experiments;&lt;/li&gt;
&lt;li&gt;explicit limitations on what each test actually proves.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PUBLIC CONTRIBUTION
        ↓
EXACT SOURCE / COMMIT
        ↓
REPRODUCIBLE TEST
        ↓
PUBLIC EVIDENCE
        ↓
BOUNDED TESTED STATUS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not every contributor needs to work inside the same repository.&lt;/p&gt;

&lt;p&gt;Some contributors maintain their own projects, forks, research packages or independent runtime environments. Instead of absorbing those projects into MyZubster, we can build &lt;strong&gt;small, reproducible bridges between independently owned work&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current tested contributor checkpoints
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Nicola / N4K48
&lt;/h3&gt;

&lt;p&gt;GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nicolaususnicola-lgtm" rel="noopener noreferrer"&gt;@nicolaususnicola-lgtm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;External project: &lt;a href="https://github.com/nicolaususnicola-lgtm/myzubster-mvp" rel="noopener noreferrer"&gt;nicolaususnicola-lgtm/myzubster-mvp&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nicola's work became one of our first runtime interoperability experiments.&lt;/p&gt;

&lt;p&gt;The bounded test used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MyZubster VPS
    ↓
authenticated HTTPS broker
    ↓
contributor-controlled N4K48 agent
    ↓
local contributor catalog
    ↓
bounded result returned to MyZubster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The participant-controlled environment successfully returned the expected result to the bridge.&lt;/p&gt;

&lt;p&gt;Evidence state:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TESTED&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Canonical MyZubster evidence merge:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;0c00836a96b36d7c64de098dd26d39ac5ddaf98a&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Nicola also authored MyZubster PR #1460, merged at:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ee8c15d4b26824b75345c2eebc62ac034058b84a&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This checkpoint demonstrates a bounded runtime bridge.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; by itself establish complete decentralization, direct P2P networking, security certification, employment, payment or governance authority.&lt;/p&gt;




&lt;h3&gt;
  
  
  Shweta-singh24 / MyZubsterGateway
&lt;/h3&gt;

&lt;p&gt;GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Shweta-singh24" rel="noopener noreferrer"&gt;@Shweta-singh24&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;External project: &lt;a href="https://github.com/Shweta-singh24/MyZubsterGateway" rel="noopener noreferrer"&gt;Shweta-singh24/MyZubsterGateway&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Shweta's external MyZubsterGateway work provided a good opportunity to test something different: &lt;strong&gt;can we independently reproduce a security/policy checkpoint from an external repository without pretending that the code has been deployed or merged upstream?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We pinned the verifier to this exact source commit:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;82461433e0c5bfee9aa369b4a71e9331261cf803&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The upstream MyZubsterGateway PR #1385 is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLOSED_UNMERGED&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Our independent VPS verifier reproduced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;verifier status: TESTED
policy checks:   14/14 PASS
syntax checks:    5/5 PASS
wiring checks:    6/6 true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The wiring checkpoint covered bounded Tari/XMR &lt;code&gt;jurisdictionGate&lt;/code&gt; paths.&lt;/p&gt;

&lt;p&gt;The MyZubster evidence PR #1575 passed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CI – Test e Lint&lt;/li&gt;
&lt;li&gt;Security Audit&lt;/li&gt;
&lt;li&gt;MYZ-164 Seller Free policy&lt;/li&gt;
&lt;li&gt;Continuous Evidence Gate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and was squash-merged at:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dc4fb1e3c403f6e554c2402ec9fb4c5dfd93e593&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This means we independently reproduced that exact checkpoint.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; mean the upstream PR was merged, the system was deployed to production, legal compliance was certified or a security certification was issued.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;




&lt;h3&gt;
  
  
  Open Period Care / khongten124
&lt;/h3&gt;

&lt;p&gt;GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/khongten124" rel="noopener noreferrer"&gt;@khongten124&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;External project: &lt;a href="https://github.com/khongten124/myzubster" rel="noopener noreferrer"&gt;khongten124/myzubster&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open Period Care is an example of a different type of interoperability.&lt;/p&gt;

&lt;p&gt;Instead of a runtime security or API bridge, this work connects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PUBLIC RESEARCH
      ↓
EVIDENCE PACKAGE
      ↓
KNOWLEDGE CARDS
      ↓
STRUCTURED RETRIEVAL
      ↓
CIRCULAR CARE / KNOWLEDGE GRAPH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contributor's research package focuses on areas including materials, sustainable health technologies, technical documentation and evidence analysis.&lt;/p&gt;

&lt;p&gt;Relevant MyZubster contribution anchors:&lt;/p&gt;

&lt;p&gt;PR #1451:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fc4a5cd30c854a242e6ed39e757c77f463c5d231&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;PR #1489:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;6d88448821f25a252a79a777101ef904af8bbccb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The contributor-scoped semantic bridge has been recorded as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TESTED&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This checkpoint is about evidence normalization, provenance and retrieval.&lt;/p&gt;

&lt;p&gt;It does not establish a medical credential, professional certification, physical recycling proof or production blockchain implementation.&lt;/p&gt;




&lt;h2&gt;
  
  
  More tested contributions inside MyZubster
&lt;/h2&gt;

&lt;p&gt;Not every tested contributor checkpoint comes from an external repository.&lt;/p&gt;

&lt;p&gt;Several contributors have testable work directly inside the canonical MyZubster repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aming9303 — signed payment webhooks
&lt;/h3&gt;

&lt;p&gt;GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Aming9303" rel="noopener noreferrer"&gt;@Aming9303&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PR #891 implemented signed lifecycle webhooks.&lt;/p&gt;

&lt;p&gt;Canonical merge:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;be78e0cf9081c3346aa0c61e022acd297d745619&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Independent regression testing covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HMAC-SHA256 signing;&lt;/li&gt;
&lt;li&gt;stable delivery IDs across retries;&lt;/li&gt;
&lt;li&gt;unsigned endpoint rejection;&lt;/li&gt;
&lt;li&gt;one-time replay claims;&lt;/li&gt;
&lt;li&gt;stale-event rejection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5/5 targeted tests passed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Status:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TESTED — signed payment-webhook regression&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This does not establish external receiver deployment, settlement, wallet security or broader infrastructure certification.&lt;/p&gt;




&lt;h3&gt;
  
  
  wasim-builds — fail-closed admin authentication
&lt;/h3&gt;

&lt;p&gt;GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/wasim-builds" rel="noopener noreferrer"&gt;@wasim-builds&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PR #860:&lt;/p&gt;

&lt;p&gt;Canonical merge:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;9c36d5be450e12345ff9251a40ab4df38839a7fe&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The independent checkpoint reproduced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unconfigured → 503
missing      → 401
wrong        → 401
correct      → 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4/4 targeted tests passed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Status:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TESTED — fail-closed admin-auth regression&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Again, the claim is deliberately bounded to that regression behavior.&lt;/p&gt;




&lt;h3&gt;
  
  
  foxxx009 — KPI/evidence framework and automated testing
&lt;/h3&gt;

&lt;p&gt;GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/foxxx009" rel="noopener noreferrer"&gt;@foxxx009&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PR #894 — KPI/evidence framework:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;50f70aab6dc9a909f65b81cb70d932706143afec&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;PR #259 — historical GitHubMonitor test contribution:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;1c78fadd608e8c8acaeaeaf00d448676f82c3f91&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The KPI/evidence framework was independently reproduced with synthetic data.&lt;/p&gt;

&lt;p&gt;The historical #259 test suite was later re-enabled through maintainer integration PR #1526:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;f8cbdee1e50cfbfa6925b4bc1014ca75ed5e90d1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That attribution distinction is intentional:&lt;/p&gt;

&lt;h1&gt;
  
  
  259 is contributor-authored work.
&lt;/h1&gt;

&lt;h1&gt;
  
  
  1526 is a maintainer-authored integration repair that made the historical test suite runnable again.
&lt;/h1&gt;

&lt;p&gt;Good provenance should preserve that difference.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why external repositories matter
&lt;/h2&gt;

&lt;p&gt;A healthy open-source ecosystem should not require every contributor to surrender project independence.&lt;/p&gt;

&lt;p&gt;We would rather support a model like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONTRIBUTOR PROJECT A ───┐
                         │
CONTRIBUTOR PROJECT B ───┼── reproducible bridges ── MYZUBSTER
                         │
CONTRIBUTOR PROJECT C ───┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;External projects can remain under their own ownership, architecture and release process.&lt;/p&gt;

&lt;p&gt;MyZubster only needs a bounded interface or reproducible artifact when interoperability is useful.&lt;/p&gt;

&lt;p&gt;Current examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nicolaususnicola-lgtm/myzubster-mvp" rel="noopener noreferrer"&gt;nicolaususnicola-lgtm/myzubster-mvp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Shweta-singh24/MyZubsterGateway" rel="noopener noreferrer"&gt;Shweta-singh24/MyZubsterGateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/khongten124/myzubster" rel="noopener noreferrer"&gt;khongten124/myzubster&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the canonical ecosystem repository remains:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster" rel="noopener noreferrer"&gt;MyZubster-Ecosystem/myzubster&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Evidence-first does not mean claim-first
&lt;/h2&gt;

&lt;p&gt;One principle has become increasingly important during this work:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A passing test should prove exactly what it tested — and nothing more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TESTED
≠ deployed

MERGED
≠ production-ready

security regression PASS
≠ security certification

research evidence
≠ professional credential

blockchain attestation
≠ proof that a physical event happened

contribution
≠ employment

verified contribution
≠ payment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Being precise about these boundaries makes public technical evidence more credible, not less.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where we want to go next
&lt;/h2&gt;

&lt;p&gt;The long-term direction is a contributor network where independent developers, researchers and maintainers can expose small interoperable capabilities without centralizing everything into one codebase.&lt;/p&gt;

&lt;p&gt;A future graph could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Contributor project
      ↓
public commit
      ↓
reproducible capability
      ↓
signed / bounded request
      ↓
another independent node
      ↓
public evidence result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting milestone is not simply "more contributors".&lt;/p&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;more independently controlled projects that can prove small things to each other reproducibly.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That creates a much stronger foundation for open-source collaboration, decentralized infrastructure and evidence-driven automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore the project
&lt;/h2&gt;

&lt;p&gt;Canonical repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/MyZubster-Ecosystem/myzubster" rel="noopener noreferrer"&gt;github.com/MyZubster-Ecosystem/myzubster&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;External contributor projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nicolaususnicola-lgtm/myzubster-mvp" rel="noopener noreferrer"&gt;Nicola / N4K48 — myzubster-mvp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Shweta-singh24/MyZubsterGateway" rel="noopener noreferrer"&gt;Shweta-singh24 — MyZubsterGateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/khongten124/myzubster" rel="noopener noreferrer"&gt;khongten124 — Open Period Care research project&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you maintain an independent open-source project and want to explore a small, reproducible interoperability checkpoint instead of a vague "integration", that is exactly the kind of experiment we are interested in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One project. One bounded capability. One reproducible checkpoint. One public evidence trail.&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  opensource #github #decentralization #security
&lt;/h1&gt;

</description>
      <category>myzubster</category>
      <category>interoperability</category>
      <category>contributor</category>
      <category>network</category>
    </item>
    <item>
      <title>🧠 **Teaching an AI Assistant What It Is Allowed to Know, Claim, and Do**</title>
      <dc:creator>Daniel Ioni</dc:creator>
      <pubDate>Thu, 08 Oct 2026 16:47:49 +0000</pubDate>
      <link>https://dev.to/danielioni/teaching-an-ai-assistant-what-it-is-allowed-to-know-claim-and-do-17o0</link>
      <guid>https://dev.to/danielioni/teaching-an-ai-assistant-what-it-is-allowed-to-know-claim-and-do-17o0</guid>
      <description>&lt;p&gt;🧠 &lt;strong&gt;Teaching an AI Assistant What It Is Allowed to Know, Claim, and Do&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’m working on a new MyZubster milestone around Zorgax.&lt;/p&gt;

&lt;p&gt;The interesting problem is not simply:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“How do we give an AI assistant more knowledge?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The harder problem is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How do we define what it is actually allowed to do with that knowledge — and how do we prove those capabilities?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is what PR #1573 is about.&lt;/p&gt;

&lt;h3&gt;
  
  
  Separating contributor competence from AI capability
&lt;/h3&gt;

&lt;p&gt;One of the design decisions I wanted to make explicit is that these are two different things.&lt;/p&gt;

&lt;p&gt;A contributor record answers questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who produced this work?&lt;/li&gt;
&lt;li&gt;Which repository or pull request supports it?&lt;/li&gt;
&lt;li&gt;What was documented, recorded or tested?&lt;/li&gt;
&lt;li&gt;What is the evidence state?&lt;/li&gt;
&lt;li&gt;What are the limitations?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Zorgax capability answers something else:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which evidence may Zorgax consume?&lt;/li&gt;
&lt;li&gt;Which actions may it perform?&lt;/li&gt;
&lt;li&gt;Which claims must it not make?&lt;/li&gt;
&lt;li&gt;Which source state is required?&lt;/li&gt;
&lt;li&gt;Does the capability need a bounded test before it can be called &lt;code&gt;TESTED&lt;/code&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;If a contributor has expertise or evidence in a domain, Zorgax should not automatically inherit that expertise as an unrestricted claim.&lt;/p&gt;

&lt;h3&gt;
  
  
  A machine-readable capability model
&lt;/h3&gt;

&lt;p&gt;The new schema is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;myzubster.zorgax-capability.v1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A capability defines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inputs
→ allowed actions
→ prohibited claims
→ evidence requirements
→ linked evidence
→ limitations
→ capability state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initial states are intentionally conservative:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Contributor Profile Assistant
→ DOCUMENTED

Research Evidence Navigator
→ TESTED

Interoperability Checkpoint Assistant
→ DOCUMENTED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only one of them starts as &lt;code&gt;TESTED&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is deliberate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: contributor profiles
&lt;/h3&gt;

&lt;p&gt;The Contributor Profile Assistant may use approved public evidence to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;summarize declared professional interests;&lt;/li&gt;
&lt;li&gt;prepare a profile draft;&lt;/li&gt;
&lt;li&gt;identify provenance;&lt;/li&gt;
&lt;li&gt;suggest bounded onboarding steps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it must not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;invent qualifications;&lt;/li&gt;
&lt;li&gt;infer employment;&lt;/li&gt;
&lt;li&gt;infer certification;&lt;/li&gt;
&lt;li&gt;infer Passport / LIFE / DAO participation;&lt;/li&gt;
&lt;li&gt;publish profile information without the required approval.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if someone says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I am interested in MyZubster usability testing”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Zorgax can preserve and structure that information.&lt;/p&gt;

&lt;p&gt;It cannot silently convert it into:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Certified software tester.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Example: research evidence
&lt;/h3&gt;

&lt;p&gt;The Research Evidence Navigator is designed around another rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;evidence state must survive retrieval.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If a research Knowledge Card is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SUPPORTED&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;then after Zorgax retrieves or summarizes it, it is still:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SUPPORTED&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;not:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;TESTED&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;not:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;scientifically validated&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;not:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;clinically certified&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This capability already has a bounded contributor-scoped checkpoint behind it, which is why its current state can be &lt;code&gt;TESTED&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capability testing
&lt;/h3&gt;

&lt;p&gt;The model I’m moving toward is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Capability definition
        ↓
Allowed evidence
        ↓
Fixed / bounded procedure
        ↓
Observed result
        ↓
PASS / FAIL / PARTIAL
        ↓
Evidence state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Documentation alone is not enough.&lt;/p&gt;

&lt;p&gt;A capability should become &lt;code&gt;TESTED&lt;/code&gt; only when a reproducible bounded checkpoint supports it.&lt;/p&gt;

&lt;h3&gt;
  
  
  CI enforcement
&lt;/h3&gt;

&lt;p&gt;I also added a validator for the Zorgax capability registry and connected it to the existing Continuous Evidence Gate.&lt;/p&gt;

&lt;p&gt;The validator checks things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;schema validity;&lt;/li&gt;
&lt;li&gt;duplicate capability IDs;&lt;/li&gt;
&lt;li&gt;registry ↔ file consistency;&lt;/li&gt;
&lt;li&gt;status consistency;&lt;/li&gt;
&lt;li&gt;whether a &lt;code&gt;TESTED&lt;/code&gt; capability actually links to bounded &lt;code&gt;TESTED&lt;/code&gt; checkpoint evidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So capability state is not just prose anymore.&lt;/p&gt;

&lt;p&gt;It is becoming something CI can reject.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this matters
&lt;/h3&gt;

&lt;p&gt;A lot of AI systems are described in vague terms:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The assistant knows X.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think that framing is too loose for contributor-driven systems.&lt;/p&gt;

&lt;p&gt;I prefer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source
→ provenance
→ evidence state
→ allowed capability
→ guardrails
→ bounded test
→ observed result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes it easier to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why is Zorgax allowed to say this?&lt;/li&gt;
&lt;li&gt;Which source supports it?&lt;/li&gt;
&lt;li&gt;Is the information self-declared, supported, recorded or tested?&lt;/li&gt;
&lt;li&gt;What is Zorgax explicitly prohibited from inferring?&lt;/li&gt;
&lt;li&gt;Has this specific capability actually been tested?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Current roadmap
&lt;/h3&gt;

&lt;p&gt;The broader MyZubster path is becoming:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Contributor evidence
→ machine-validatable checkpoint
→ competence / provenance registry
→ Zorgax capability registry
→ bounded capability tests
→ Passport / Knowledge Graph / workflows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;without collapsing all of those states into one generic “verified” label.&lt;/p&gt;

&lt;p&gt;PR #1573 is the first implementation of that capability layer.&lt;/p&gt;

&lt;p&gt;Once the checks are green and the PR is merged, I’ll publish the canonical merge commit and the final capability states.&lt;/p&gt;

&lt;p&gt;The principle behind the whole thing is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI capability should be bounded by evidence, not by confidence.&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  SoftwareEngineering #AIEngineering #AIGovernance #DevOps #JSONSchema #CI #OpenSource #SystemDesign #EvidenceFirst #DeveloperTools #MyZubster
&lt;/h1&gt;

</description>
      <category>ai</category>
      <category>assistante</category>
      <category>know</category>
      <category>claim</category>
    </item>
  </channel>
</rss>
