<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/security-baked-into-the-jvm-sixteen-subjects-on-the-wire-uzxglis4o" -->

---
title: Security Baked Into the JVM: sixteen Subjects on the wire
description: A deep technical dive into how JGDMS (an Apache River/Jini fork) with the DirtyChai security extension carries user identity chains across remote calls on the...
canonical: https://daily.dev/posts/security-baked-into-the-jvm-sixteen-subjects-on-the-wire-uzxglis4o
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Security Baked Into the JVM: sixteen Subjects on the wire | daily.dev
og:description: A deep technical dive into how JGDMS (an Apache River/Jini fork) with the DirtyChai security extension carries user identity chains across remote calls on the...
og:url: https://daily.dev/posts/security-baked-into-the-jvm-sixteen-subjects-on-the-wire-uzxglis4o
og:image: https://api.daily.dev/og/posts/UzxGlIS4O.png
og:image:alt: Security Baked Into the JVM: sixteen Subjects on the wire
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Security Baked Into the JVM: sixteen Subjects on the wire

**[A Java geek](https://daily.dev/sources/frankel)** · 9 min read · 2 upvotes · 1 comments

## Summary

A deep technical dive into how JGDMS (an Apache River/Jini fork) with the DirtyChai security extension carries user identity chains across remote calls on the JVM. Covers the 0x02 wire format for marshalling Subject chains, strict receiver-side limits against DoS, a fixed allowlist of trusted principal classes (never instantiating remote-supplied class names), JWT verification caveats (default verifier only checks exp/iat, not signatures), reflective tricks needed to keep code compiling on stock OpenJDK where UserSubject doesn't exist, propagating identity across thread boundaries with SubjectAwareExecutor, and unanimous permission checks across all participants in a distributed transaction via TxnManagerImpl.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://blog.frankel.ch/security-baked-into-jvm/4>

## Questions this post answers

### How does JGDMS prevent a malicious peer from making the server instantiate arbitrary principal classes over the wire?

BasicInvocationDispatcher never calls Class.forName on a remote-supplied class name; instead it holds a fixed allowlist of four principal types (X500Principal, KerberosPrincipal, SpiffePrincipal, JwtPrincipal) resolved once at class-load time. Any other class name produces an inert RemotePrincipal placeholder that matches no policy grant, since the allowlist rather than the classloader is the security boundary.

_Developers designing tamper-resistant identity propagation can find implementation patterns like this discussed on daily.dev._

### How do you propagate a caller's Subject identity to a virtual thread spawned inside a service method in Java?

A ScopedValue binding does not cross into a spawned thread by default, so a virtual thread inherits the server's worker identity rather than the caller's. SubjectAwareExecutor in net.jini.security wraps any ExecutorService, capturing the current Subject array and SecurityContext at submission time and re-establishing them via AccessController.doPrivileged on the worker thread.

_Engineers debugging identity loss across thread boundaries can track patterns like SubjectAwareExecutor on daily.dev._

### What are the limits of the 0x02 user-Subject wire format's count fields in JGDMS, and how does the receiver stay safe against a hostile peer lying about them?

The count fields (subjectCount, principalCount, jwtCount, jwtLength) are 16-bit or 8-bit and can hold almost any value written by a hostile peer, but BasicInvocationDispatcher enforces hard limits on the receiving end: at most 16 Subjects, 64 principals per Subject, 4 tokens per Subject, and 65,536 bytes per token, rejecting anything beyond with an IOException.

_Teams hardening RPC protocols against resource-exhaustion attacks can follow deep dives like this on daily.dev._

## Community discussion

Top comments from developers on daily.dev.

**@agustinbarrientos** · 0 upvotes

> I'd reject production startup when wire-supplied identity lacks a signature-verifying JWT provider. The relaxed verifier can stay behind an explicit development profile. I'd include the active verifier mode in every authorization trace.

## Similar posts on daily.dev

- [Security Baked Into the JVM: two Subjects, one call](https://daily.dev/posts/security-baked-into-the-jvm-two-subjects-one-call-fifkkyz8a) · A Java geek · 2 upvotes · 2 comments
- [Security Baked Into the JVM: why fork Apache River and OpenJDK?](https://daily.dev/posts/security-baked-into-the-jvm-why-fork-apache-river-and-openjdk--jddvz9ame) · A Java geek · 1 upvotes · 0 comments
- [Security Baked Into the JVM: no single party controls the outcome](https://daily.dev/posts/security-baked-into-the-jvm-no-single-party-controls-the-outcome-8thqti56v) · A Java geek · 0 upvotes · 0 comments
- [JDK 24 Security Enhancements](https://daily.dev/posts/jdk-24-security-enhancements-tln02dr7b) · Baeldung · 0 upvotes · 0 comments

---

Tags: [#java](https://daily.dev/tags/java), [#authentication](https://daily.dev/tags/authentication), [#distributed-systems](https://daily.dev/tags/distributed-systems), [#jvm](https://daily.dev/tags/jvm)

[View this post on daily.dev](https://daily.dev/posts/security-baked-into-the-jvm-sixteen-subjects-on-the-wire-uzxglis4o)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Security Baked Into the JVM: sixteen Subjects on the wire","url":"https://daily.dev/posts/security-baked-into-the-jvm-sixteen-subjects-on-the-wire-uzxglis4o","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/security-baked-into-the-jvm-sixteen-subjects-on-the-wire-uzxglis4o"},"datePublished":"2026-08-23T16:13:04.726Z","dateModified":"2026-09-14T07:06:46.330Z","description":"A deep technical dive into how JGDMS (an Apache River/Jini fork) with the DirtyChai security extension carries user identity chains across remote calls on the...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/d0995ad88acdc16b26a204e0ac9fbfda?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/d0995ad88acdc16b26a204e0ac9fbfda?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"A Java geek","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"A Java geek","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/b7f3fdbf443347ea84705578a195085b","url":"https://daily.dev/sources/frankel"},"commentCount":1,"discussionUrl":"https://daily.dev/posts/security-baked-into-the-jvm-sixteen-subjects-on-the-wire-uzxglis4o","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":2},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":1}],"keywords":"java,authentication,distributed-systems,jvm","timeRequired":"PT9M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"A Java geek","item":"https://daily.dev/sources/frankel"},{"@type":"ListItem","position":3,"name":"Security Baked Into the JVM: sixteen Subjects on the wire"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/security-baked-into-the-jvm-sixteen-subjects-on-the-wire-uzxglis4o","comment":[{"@type":"Comment","text":"I’d reject production startup when wire-supplied identity lacks a signature-verifying JWT provider. The relaxed verifier can stay behind an explicit development profile. I’d include the active verifier mode in every authorization trace.","datePublished":"2026-08-30T16:40:45.194Z","url":"https://daily.dev/posts/UzxGlIS4O#c-0vfkCryU7","author":{"@type":"Person","name":"Agustin Barrientos","url":"https://daily.dev/agustinbarrientos","image":"https://media.daily.dev/image/upload/s--5ayxQnqn--/f_auto/v1788281802/avatars/avatar_wQYYVe5Tbj0NJ7C7qPoa8?_a=BAMAMicg0"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/security-baked-into-the-jvm-sixteen-subjects-on-the-wire-uzxglis4o#faq","mainEntity":[{"@type":"Question","name":"How does JGDMS prevent a malicious peer from making the server instantiate arbitrary principal classes over the wire?","acceptedAnswer":{"@type":"Answer","text":"BasicInvocationDispatcher never calls Class.forName on a remote-supplied class name; instead it holds a fixed allowlist of four principal types (X500Principal, KerberosPrincipal, SpiffePrincipal, JwtPrincipal) resolved once at class-load time. Any other class name produces an inert RemotePrincipal placeholder that matches no policy grant, since the allowlist rather than the classloader is the security boundary. Developers designing tamper-resistant identity propagation can find implementation patterns like this discussed on daily.dev."}},{"@type":"Question","name":"How do you propagate a caller's Subject identity to a virtual thread spawned inside a service method in Java?","acceptedAnswer":{"@type":"Answer","text":"A ScopedValue binding does not cross into a spawned thread by default, so a virtual thread inherits the server's worker identity rather than the caller's. SubjectAwareExecutor in net.jini.security wraps any ExecutorService, capturing the current Subject array and SecurityContext at submission time and re-establishing them via AccessController.doPrivileged on the worker thread. Engineers debugging identity loss across thread boundaries can track patterns like SubjectAwareExecutor on daily.dev."}},{"@type":"Question","name":"What are the limits of the 0x02 user-Subject wire format's count fields in JGDMS, and how does the receiver stay safe against a hostile peer lying about them?","acceptedAnswer":{"@type":"Answer","text":"The count fields (subjectCount, principalCount, jwtCount, jwtLength) are 16-bit or 8-bit and can hold almost any value written by a hostile peer, but BasicInvocationDispatcher enforces hard limits on the receiving end: at most 16 Subjects, 64 principals per Subject, 4 tokens per Subject, and 65,536 bytes per token, rejecting anything beyond with an IOException. Teams hardening RPC protocols against resource-exhaustion attacks can follow deep dives like this on daily.dev."}}]}
```

