All notable changes to the Atmosphere Framework are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Tool-call admission seam (
1def61ddf0) —PolicyAdmissionGate.admitToolCallbuilds a syntheticAiRequestwhose metadata carriestool_name,action, and an argument preview so MS-schema rules overtool_namefire before the tool's executor runs.ToolExecutionHelperconsults the gate on every@AiTooldispatch; the canonical MS example{field: tool_name, operator: eq, value: delete_database, action: deny}fires without operator plumbing. OWASP A02 upgraded from PARTIAL to COVERED. @AgentScope.postResponseCheck(2913da1b81) — when enabled on a high-stakes scope,ScopePolicyre-classifies the streamed response text against the declared purpose. OUT_OF_SCOPE responses become Deny with apost-response:prefix; errors fail-open on the response path (bytes already on the wire).POLITE_REDIRECTbreaches downgrade to Deny because Transform can't rewind a stream.- Cross-provider governance contract (
613d216019) —AbstractAgentRuntimeContractTest.policyDenyBlocksRuntimeExecuteis inherited by all seven runtime adapters (Built-in, Spring AI, LangChain4j, ADK, Embabel, Koog, Semantic Kernel); the "deny before runtime" guarantee is now a build-time invariant for each provider. - Per-request
ScopePolicyinstall (334bde4969) — an interceptor can write aScopeConfigunderScopePolicy.REQUEST_SCOPE_METADATA_KEYand the pipeline / streaming session / admission gate install a transientScopePolicyahead of endpoint-level policies for that one turn. Classroom sample uses this for per-room scope (math / code / science / general) — one@AiEndpointhosts four personas, each with its own purpose and forbidden-topic set.perRequestScopeBlocksRuntimeExecuteextends the cross-provider contract to the per-request path. - Admin console governance views — three Vue views under the existing
Atmosphere Console (
/atmosphere/console/) poll/api/admin/governance/{policies,decisions,owasp}on live intervals. Tabs auto-hide when governance is not installed. Verified end-to-end against the classroom sample via chrome-devtools (tabs render, OWASP matrix shows 7 Covered / 1 Partial / 1 Design / 1 Not-addressed, zero console errors). - Persistent
AuditSinkSPI —GovernanceDecisionLog.addSink(AuditSink)fans every admission decision out to registered sinks while keeping the ring buffer authoritative for the admin console. Sink failures are isolated: one unreachable Kafka broker does not take down the pipeline.AsyncAuditSinkwraps a blocking delegate with a bounded drop-on-full queue so the admission thread never blocks on IO (Backpressure invariant #3). Two reference modules ship:atmosphere-ai-audit-kafka(KafkaAuditSink→ JSON to any topic) andatmosphere-ai-audit-postgres(JdbcAuditSink→ JDBC upsert with schema auto-create, works against any JSR-221DataSource; tests exercise H2 in-memory). The JSON shape matches MS Agent Governance Toolkit'saudit_entryso downstream SIEM consumers of either system can read both.
@AgentScopeannotation +ScopeGuardrailSPI (ba7ddf3688) — architectural goal-hijacking prevention. Annotation declarespurpose,forbiddenTopics,onBreach(POLITE_REDIRECT / DENY / CUSTOM_MESSAGE), andtier(RULE_BASED / EMBEDDING_SIMILARITY default / LLM_CLASSIFIER opt-in).ScopePolicymapsScopeGuardrailoutcomes to admit/transform/deny semantics per the breach policy.RuleBasedScopeGuardrailships with built-in hijacking probes for code, medical, legal, and financial patterns so the McDonald's failure mode is caught without operator-declared topic enumeration.- System-prompt hardening + endpoint auto-wiring (
a11239cac3) —AiEndpointProcessorreads@AgentScopeon the endpoint class and auto-installs aScopePolicyahead of user-declared policies;AiPipelineprepends an unbypassable scope-confinement preamble to the system prompt on everyexecute()call, surviving sample-level substitutions. - Sample-hygiene CI lint + 12-sample retrofit (
287a5f9b71) —SampleAgentScopeLintTestwalkssamples/and fails the build on any@AiEndpointmissing@AgentScope(or lacking a non-blankjustificationwhenunrestricted = true). All 12 existing sample endpoints retrofitted — 11 declareunrestricted = truewith specific justifications (production deployments replace with a scoped@AgentScope);ReviewExtractordeclares a real scoped purpose. - Embedding-similarity scope tier (
2c856bd00d) — default tier. Resolves anEmbeddingRuntimevia ServiceLoader; caches the purpose vector (and every forbidden-topic vector) on first use; rejects when cosine similarity falls belowsimilarityThreshold. Absent runtime admits-with-warning so the rule-based tier remains a safe fallback. - LLM-classifier scope tier (
5b8b6f51da) — opt-in tier for high-stakes scopes. Zero-shot YES/NO classifier over the resolvedAgentRuntime; tolerant parser handles**YES**/YES./*no*/ "Not sure" edge cases; ambiguous verdicts fall through to admit so LLM quirks don't over-reject; timeouts and runtime errors fail-closed at theScopePolicylayer. - Governance audit trail (
a534f5e462) —AuditEntryrecords everyGovernancePolicy.evaluatedecision with identity, reason, context snapshot (redaction-safe: truncated message, primitive-only metadata), andevaluation_ms.GovernanceDecisionLogis a thread-safe ring buffer (default 500 entries) installed viaGovernanceDecisionLog.install(capacity). Surfaced viaGET /api/admin/governance/decisions?limit=N.GovernanceTraceremits an OpenTelemetry span per evaluation through reflective classpath detection — OTel stays an optional dependency. - ms-governance-chat feature-parity retrofit (
3fc8ead4cb) — the sample now declares@AgentScope(purpose = "Customer support for Example Corp — orders, billing, ...")and loads 9 MS-schema rules mirroring MS'scustomer-service/main.pyexample (destructive SQL, legal/media/exec escalation, human-request auto-escalate, PII shapes, password disclosure, discount-limit enforcement, plus anaudit-action rule for code-request probing). - OWASP Agentic Top-10 self-assessment matrix (
712c57e4e8) —OwaspAgenticMatrix.MATRIXpins 10 rows (6 COVERED, 2 PARTIAL, 1 DESIGN, 1 NOT_ADDRESSED) with evidence classes, test references, and consumer grep patterns per row.OwaspMatrixPinTestfails the build when any evidence class is renamed or removed — structural answer to the v4 §4 discipline risk. Served over HTTP atGET /api/admin/governance/owaspforagt verify-style compliance consumers.
GovernancePolicySPI (0ace2b6947) — declarative policy identity (name/source/version) plusPolicyContext→PolicyDecisionevaluation. Vocabulary aligned with OPA/Rego and Microsoft Agent Governance Toolkit at the evaluate-decision level (admit/deny/ transform). The SPI is strictly additive — existingAiGuardrailwiring keeps working unchanged.GuardrailAsPolicy+PolicyAsGuardrailadapters (efefaea40a) — every existingAiGuardrailis reachable as aGovernancePolicyviaGuardrailAsPolicy; policies land on the currentAiPipelineadmission seam viaPolicyAsGuardrail.Transformdecisions on the post-response path are downgraded topasswith a warning (streamed text is not retroactively rewritable).- YAML
PolicyParser+PolicyRegistry(83e5c2dafd) — default parser reads YAML via SnakeYAML'sSafeConstructor(no arbitrary class instantiation). Built-in types:pii-redaction,cost-ceiling,output-length-zscore— factories wrap the shipped guardrails with the identity from the YAML entry.modules/aideclares SnakeYAML as an explicit runtime dep so bare-JVM / Jetty-embedded deployments get the parser out-of-the-box. Parser discovered via ServiceLoader; additional formats (Rego, Cedar) plug in by shipping anotherPolicyParserservice entry. - Native
AiPipelinewiring (9ac9ed1d6c) —AiPipelineaccepts aList<GovernancePolicy>on a new constructor, evaluates them in a dedicated pre-admission loop (fail-closed on exception per Correctness Invariant #2), and merges them onto the existingGuardrailCapturingSessionfor post-response viaPolicyAsGuardrail.pipeline.policies()accessor exposes the installed list so admin surfaces can enumerate them without re-parsing YAML. Response cache skips when policies are present so each turn re-evaluates. - Spring + endpoint-processor bridge (
9d7b75be78) —AiEndpointProcessor.instantiatePolicies()merges ServiceLoader andPOLICIES_PROPERTYsources with dedup byname(); policies are wrapped throughPolicyAsGuardrailand threaded onto every@AiEndpointin the app.AtmosphereAiAutoConfigurationnow bridges Spring-managedGovernancePolicybeans onto the same property so the default path "drop a YAML file on the classpath" just works. Parity test (PolicyPlaneSourceParityTest) pins YAML / programmatic / ServiceLoader sources to identical admission decisions. - Classroom sample retrofit (
aaac15f725) —samples/spring-boot-ai-classroom/src/main/resources/atmosphere-policies.yamldeclares a PII redaction and a z-score drift detector;PoliciesConfigreads it at startup and publishes the list toPOLICIES_PROPERTY. Demonstrates the key promise: change YAML, restart, governance changes — zero code edits. - Admin introspection (
b973aa2828) —GovernanceControllerreadsPOLICIES_PROPERTYfor per-policy identity and distinct-source counts;/api/admin/governance/policiesand/api/admin/governance/summaryHTTP endpoints expose the live list;AtmosphereAdmin.overview()reports the policy count alongside the AI runtime name. - Microsoft Agent Governance Toolkit YAML parity —
YamlPolicyParserauto-detects the MS schema (documents with top-levelrules:) and produces aMsAgentOsPolicythat preserves MS's first-match-by-priority rule-evaluation semantic. All nine comparison operators (eq,ne,gt,lt,gte,lte,in,contains,matches) and all four actions (allow,deny,audit,block) are honored. Context map bridgesAiRequestfields (message,model,user_id, …) and every metadata entry to rule field names.MsAgentOsYamlConformanceTestloads MS's own example YAMLs (copied unmodified frommicrosoft/ agent-governance-toolkit@April-2026) and asserts byte-for-byte interop.
AgentStateSPI (a0fd3fc48c) — unifies conversation history, durable facts, daily notes, working memory, and hierarchical rules under one runtime-agnostic interface. File-backed default (FileSystemAgentState) reads and writes an OpenClaw-compatible Markdown workspace.AutoMemoryStrategypluggable with four built-ins (EveryNTurns,LlmDecided,SessionEnd,Hybrid).AgentStateConversationMemoryis a thin shim over the legacyAiConversationMemory.AdkAgentRuntimeseeds its ADKSessionfromcontext.history()(closes Correctness Invariant #5 gap whereCONVERSATION_MEMORYwas advertised but silently dropped).AgentWorkspaceSPI (d4b3e341c7) — agent-as-artifact. ServiceLoader discovery withOpenClawWorkspaceAdapter+AtmosphereNativeWorkspaceAdapter. OpenClaw canonical layout runs on Atmosphere without conversion.ProtocolBridgeSPI (853cccc4aa) —InMemoryProtocolBridgeelevated to first-class bridge, same footing as wire bridges.ProtocolBridgeRegistryenumerates active bridges.AiGatewayfacade (4d48c3eb4a,43870cb537) — single admission point for outbound LLM calls.PerUserRateLimiter, pluggableCredentialResolverandGatewayTraceExporter.BuiltInAgentRuntimenow routes every dispatch throughAiGatewayHolder.get().admit(...)— Correctness Invariant #3 enforced at the runtime boundary.AgentIdentitySPI (f4df5603a7) — per-user identity, permissions, credentials, audit, session sharing.PermissionModelayers over per-tool@RequiresApproval.AtmosphereEncryptedCredentialStoreuses AES-GCM with a 256-bit key and per-entry random IV; decryption failure is fail-closed.ToolExtensibilityPointSPI (59f7ecd197) — bounded tool discovery (ToolIndex+DynamicToolSelector) and pluggable per-user MCP trust (McpTrustProviderwithCredentialStoreBackeddefault).SandboxSPI (818f531216,1e2daa1143) — pluggable isolated execution.DockerSandboxProviderdefault + dev-onlyInProcessSandboxProvider.@SandboxToolannotation. Default limits 1 CPU · 512 MB · 5 min · no network.NetworkPolicyenum (NONE/GIT_ONLY/ALLOWLIST/FULL) replaces the boolean network flag; Docker provider labels containers with the resolved policy.AgentResumeHandle+RunRegistry(2ae4e8835b,27425b15f6) — mid-stream reconnect primitive with boundedRunEventReplayBuffer.StreamingSession.runId()default method returns the id registered withRunRegistry;DurableSessionInterceptorstashes theX-Atmosphere-Run-Idheader in a request attribute so the ai module can reattach without the durable-sessions module depending on atmosphere-ai.- Wire
ProtocolBridgeimplementations (74d3ecbd6e) for MCP, A2A, AG-UI, and gRPC so the admin control plane can answer "which agents are reachable via which protocol?" across every transport.
spring-boot-personal-assistant(2a7ae59a41) — primary coordinator delegates to scheduler / research / drafter crew viaInMemoryProtocolBridge. Ships an OpenClaw-compatible workspace (AGENTS.md/SOUL.md/USER.md/IDENTITY.md/MEMORY.md) plus Atmosphere extension files (CHANNELS.md/MCP.md/PERMISSIONS.md).spring-boot-coding-agent(ae9c2e174c) — clones a repo into a Docker sandbox and reads files.SandboxProviderdiscovered viaServiceLoader; defaults to Docker, falls back to in-process for dev.
ControlAuthorizer.DENY_ALLandREQUIRE_PRINCIPALas explicit admin-plane baselines alongside the existingALLOW_ALL.ALLOW_ALLis documented as non-production; operators wireREQUIRE_PRINCIPALon top of their transport auth (Spring Security, Quarkus security) for production deployments.
BusinessMetadata— standard keys (business.tenant.id,business.customer.id,business.session.revenue,business.event.kind, ...) with anEventKindenum. Published to SLF4J MDC on the dispatching virtual thread and cleared infinallyso Dynatrace / Datadog / OTel log exporters propagate tenant + customer + revenue tags onto the active span for every agent turn.FactResolverSPI +DefaultFactResolver— injects deterministic facts (time, user identity, plan tier, customapp.*keys) into the system prompt before every turn. Resolution order matchesCoordinationJournal/AsyncSupport: framework-property bridge (Spring beans) →ServiceLoader→ process-wide holder → default. Newline / tab / control characters in values are escaped so fact values cannot reshape the instruction context.PiiRedactionGuardrail— regex-based detection of email, phone, credit card, US SSN, IPv4. Redacts on the request path, Blocks on the response path (the SPI cannot rewrite an already-emitted stream, so default-mode log-only signalling was security theatre).OutputLengthZScoreGuardrail— rolling-window drift detector; Blocks responses more than N standard deviations above the window mean. Opt-in viaatmosphere.ai.guardrails.drift.enabled=true.- Agent-to-Agent Flow Viewer —
GET /api/admin/flowandGET /api/admin/flow/{coordinationId}render theCoordinationJournalas a graph (nodes = agents, edges = dispatch count + success / failure / avg-duration). Edge attribution is keyed per-coordinationIdso concurrent tenant runs stay scoped. - Run reattach consumer —
AiEndpointHandlernow reads theX-Atmosphere-Run-Idheader on reconnection, looks up the liveAgentResumeHandleviaRunRegistry, and replays the buffered events onto the new resource. Closes the "producer present, consumer absent" gap in the original primitive wire-in.
PiiRedactionFilter(c2076a41a1) —BroadcasterFilterthat rewrites email / phone / credit-card / US SSN / IPv4 tokens in-flight before bytes reach the client. Atmosphere owns the broadcaster, so rewriting happens on the same thread that would have flushed the unfiltered bytes — a pure orchestration layer can only block a streaming response, never redact it mid-flight. Auto-installs on every present and future broadcaster whenatmosphere.ai.guardrails.pii.enabled=true. Replacement token configurable viaatmosphere.ai.guardrails.pii.replacement(default[REDACTED]). Listener ownership is symmetric — aDisposableBeanremoves the installed listener on shutdown.
CostAccountantSPI +CostCeilingAccountantimpl (1e06de99bb) — bridgesTokenUsageevents from any runtime intoCostCeilingGuardrail.addCost(tenantId, dollars). Installed automatically when both aCostCeilingGuardrailbean and aTokenPricingbean are present on the Spring Boot classpath. Closes the "observability as dashboard" gap: cumulative tenant cost now gates outbound dispatch instead of only surfacing on a Grafana panel.TokenPricingSPI — per-model dollar-per-token schedule. Applications supply their own pricing (provider quotes change); no baked-in table.CostAccountingSessiondecorator — wraps every@Promptsession inAiStreamingSession.dispatchwhenever theCostAccountantHolderis non-NOOP. CapturesTokenUsagefrom every runtime via the existingStreamingSession.usage(...)hook, routes the cost through the accountant, then forwards to the delegate so the runtime call path is unchanged. Tenant MDC is snapshotted at construction so Reactor-thread usage events don't collapse into the__default__bucket.CostCeilingGuardrail(c2076a41a1) — inspects requests before dispatch; blocks outbound@Promptwhen the tenant's cumulative cost is at or above the per-tenant budget. Per-tenant buckets keyed bybusiness.tenant.idMDC; turns without a tenant tag share a__default__bucket.resetTenant(...)/resetAll()on an operator-driven schedule for monthly billing boundaries.
OutputLengthZScoreGuardrailtenant partition (c2076a41a1) — the rolling-window drift detector now partitions its window bybusiness.tenant.id, so a noisy tenant can no longer poison other tenants' baselines. Single-tenant deployments fall back to a shared__default__bucket and behave unchanged.
RunReattachSupport(c2076a41a1) — stateless helper extracted fromAiEndpointHandler.reattachPendingRun. Replay writes the joined buffer directly toresponse.getWriter()(U+001E between events) on reconnection. Unit-tested standalone.RunEventCapturingSessionproducer wire (8156842fd4) — closes the "consumer wired, producer missing" half of the first reattach landing. Mirrors everysession.send/complete/errorinto the run'sRunEventReplayBufferso reconnecting clients have something to replay.- Reattach wire fidelity (
69d5dad403) — replay emitsAiStreamMessageJSON frames matching the live-stream schema (one parser, two paths);AiEndpointHandlerroutes timeout / exception terminals through the capturing session so replayed streams end with a proper error envelope. - P0 reattach ownership + filter-chain replay (
ffedda4b7e) — replay refuses when the reconnecting caller's resolveduserIddoes not match the run's registereduserId(closes a bearer-token cross-user leak). Anonymous runs keep the open-mode carve-out so demo deployments still work. Every replay frame is routed through the broadcaster'sBroadcastFilterchain soPiiRedactionFilterand downstream content filters apply identically to replay and live frames — a direct-writer path previously bypassed them. RunEventCapturingSession.handoff()forwarding (ff8c2d5542) — the defaultStreamingSession.handoffthrowsUnsupportedOperationException; the capturing wrapper inherited it and broke orchestration-primitives handoffs. Now delegates.
atmosphere.admin.http-read-auth-requiredopt-in flag (2d3ee5afc3) — when true,GET/HEAD/OPTIONSon/api/admin/*require the same principal chain as the write-side gate (minusControlAuthorizer). Default off so local demo consoles keep working; multi-tenant operators exposing/api/admin/*on a routable network flip one flag.AdminApiAuthFilter(Spring) +AdminReadAuthFilter(Quarkus JAX-RS@Provider) — symmetric enforcement across starters.
- Fourth principal source on Quarkus admin writes
(
b3d032d00c) —X-Atmosphere-Authheader validated via constant-time compare againstatmosphere.admin.auth.token. A synthetic principal is admitted on match. Intended for sample fixtures and operator tooling that have not yet integrated Jakarta Security; production stacks still resolve viaSecurityContext.getUserPrincipal()first. - Malformed journal timestamp returns 400 (
9aa1651f3f) — previously 200 with an error-item array, which masked client errors and broke Spring / Quarkus API parity. Now matches the SpringAtmosphereAdminEndpointbehavior (Correctness Invariant #4).
AtmosphereFaviconAutoConfiguration(98c6ae408b,2d3ee5afc3) — serves/favicon.icoand/favicon.pngwith the Atmosphere logo PNG on every app using either spring-boot starter, killing the default 404 on the admin UI, console UI, and every sample. Opt out withatmosphere.favicon.enabled=false. Ships as a nested@RestControllerinside the@AutoConfigurationclass; a@Beanfactory introduced in the initial commit produced a duplicateatmosphereFaviconControllerbean and triggeredAmbiguous mappingat startup — removed in the fix.
- SVG coordination-graph visualizer (
d1245d7780) — new admin console tab renders/api/admin/flowas a circle-layout SVG. Nodes are agents, edges carry dispatch count / success / failure / average duration (red on failure, arrowheads for direction). Optionalcoordination-iddrilldown andlookback-minutesfilter. Zero external graph library. Mirrored acrossspring-boot-starterandspring-boot3-starteradmin assets.
RuntimeGatewayAdmissionParityTest(d5f8a03174,81c135cf5c,1632360f7f) — source-level parity scan with brace-balanced method-body extraction; every*AgentRuntime.{java,kt}must calladmitThroughGatewayfrom its designated dispatch methods or the build fails. Catches a regression where a runtime's dispatch path silently bypasses rate limiting and credential policy.- Seven exec-level
*GatewayAdmissionTestfiles —SpringAiGatewayAdmissionTest(48a38d58c6); LangChain4j, ADK, Semantic Kernel (Java) and Koog, Embabel (Kotlin) together (1006a4301d); plus the existingBuiltInExecuteWithHandleGatewayTest. Each installs a countingAiGateway.GatewayTraceExporterand drivesruntime.execute(...)so an admission entry with the correct provider label is captured at the exec level, not just source-level grep. ChangelogClaimsTest(94404ce023) — pins theAgentStateOpenClaw workspace layout andRunEventReplayBufferbound so CHANGELOG-to-code drift breaks the build.
samples/spring-boot-reattach-harness(65f2f6ce5f) —SlowEmitterChatplus aSyntheticRunControllerthat pre-populates theRunRegistryso Playwright can drive theHTTP → reattachPendingRun → replayPendingRunwire with deterministic timing.e2e/tests/reattach.spec.tsruns on every push via a dedicatedfoundation-e2e.ymljob on port 8096 — the reattach contract is proven end-to-end, not just at unit level.
BusinessMdcBenchmark(82899e5145) — JMH harness pinning the cost of the per-turnbusiness.*MDC snapshot → apply → clear cycle thatAiEndpointHandler.invokePromptruns on every dispatch. Baseline, six-key production, and empty-snapshot scenarios, so a regression on the hot path shows as numbers, not intuition.
- Admin HTTP writes now enforce authentication in addition to
the feature flag.
guardWrite(HttpServletRequest, action, target)resolves a Principal from the servletUserPrincipal, the AtmosphereAuthInterceptor-set attribute, or theai.userIdattribute, then consultsControlAuthorizer. The earlier feature-flag-only gate let any anonymous caller mutate state once the flag was flipped. Correctness Invariant #6 (Security). - MCP write tools forward the authenticated principal to
ControlAuthorizer.authorize(...). Previously every tool passednull, soREQUIRE_PRINCIPALpermanently denied andALLOW_ALLpermanently admitted regardless of identity. NewIdentityAwareToolHandlerfunctional interface threads the servlet-resolved principal throughMcpProtocolHandler.executeToolCall. AiGatewayadmission on cancel-capable dispatch paths.BuiltInAgentRuntime.doExecuteWithHandleandKoogAgentRuntime.executeWithHandlenow calladmitThroughGateway— parity with the plainexecutepath so rate limits and credential policies fire on every mode (Correctness Invariant #7 — mode parity).- Business MDC lifecycle. The MDC population was previously done on the servlet thread (wrong thread — VT logs never saw it) and never cleared. Snapshot on the servlet thread, apply on the VT dispatcher with try/finally clear, so every log record during the turn carries the tags and the VT pool starts clean on the next turn.
- Flow graph attribution under interleaved coordinations.
FlowController.buildGraphpreviously carried a flatcurrentCoordinatorcursor — concurrent runs misattributed every second edge. Now maintains acoordinationId → coordinatorNamemap. - User
@AiEndpointpaths get Spring + ServiceLoader guardrails.AiEndpointProcessormerges annotation-declared guardrails withServiceLoader.load(AiGuardrail.class)and the framework-property bridge so annotation-declared endpoints are no longer starved of the auto-wired guardrail set. NewAiGuardrail.GUARDRAILS_PROPERTYmirrors theCoordinationJournalbridge key. - Foundation E2E stops skipping the Docker sandbox regression.
SKIP_SANDBOX_E2E=truepreviously hid the command-injection hardening; removed fromfoundation-e2e.ymlso the clone+read spec runs on every PR. ubuntu-latest ships with Docker — the new workflow also verifies its presence early. - Sample boot modernization.
spring-boot-coding-agentreverted fromapplication.propertiestoapplication.yml; both samples addspring-boot-starter-actuator;foundation-e2e.ymlboots via./mvnw spring-boot:runand waits on/actuator/healthviawait-oninstead of shelling out tocurland pre-building a fat jar.
- Jetty 12.0.33, Tomcat 11.0.21, Kafka 3.9.2 (
8e4b63e18a) — closes 13 Dependabot advisories (1 critical, 5 high, 3 medium). - Bouncy Castle 1.84 pinned in
dependencyManagement(246c29bc75) — closes LDAP-injection and risky-crypto advisories against the 1.82 tree thatdocker-java-core 3.7.0pulls in transitively. Provided-scope only (Docker sandbox path); no runtime fat-jar drift. - protobuf 4.34.1 pinned to match
protoc(4876779978) —grpc-protobuf 1.80.0still pulledprotobuf-java 3.25.8transitively, soprotoc 4.x-generated sources failed to compile. - MCP SDK 1.0.0 → 1.1.1 (
da57094ce6). - React / React DOM 19.2.5 in lockstep (
8226e28fcb) — React requires an exact version match betweenreactandreact-dom; a partial Dependabot bump broke every jsdom-backed test withensureCorrectIsomorphicReactVersion. HtmlEncoderregistered as a CodeQL XSS sanitizer (b5f184e417) — resolves four false-positivejava/xsscode-scanning alerts.
AdminResourcesurvivesIllegalStateException: UT000048(e2d254ad68) — resteasy-reactive dispatches on Vert.x, so@Context HttpServletRequestattribute access throws on the admin write path. Attribute access is swallowed (attributes cannot fire on Vert.x anyway) andX-Atmosphere-Authis read via@Context HttpHeaders, which works on both transports.
GrpcWasyncTransportTeststatus-poll 2s → 5s (1586d91246) — wAsync updatesSocket.status()on its dispatch thread after theCLOSEcallback returns; the 2s polling cap was too tight on JDK 26 where scheduler latency between callback and CAS is observably longer.
FileSystemAgentStatecross-scope bleed (ad850f9f35).MEMORY.mdandmemory/YYYY-MM-DD.mdnow live underusers/<userId>/agents/<agentId>/so facts never bleed across users or agents (Correctness Invariant #6, default deny on cross-scope access). Three new isolation tests cover cross-user, cross-agent, and cross-scope delete boundaries.
atmosphere newis now sample-clone based (b7f98d42f0,0b9a8f194d). The CLI no longer ships a mustache-based scaffold.atmosphere new <name> --template <t>now sparse-clones the matching sample fromcli/samples.jsonand rewrites the clonedpom.xmlso itsorg.atmosphere:atmosphere-projectparent resolves from Maven Central (pins the version from SNAPSHOT to the release incli/samples.json, drops the reactor-relative<relativePath>, disables repo-local checkstyle/pmd bindings). The resulting project compiles standalone with plainmvn compile.- Nine templates in
cli/atmospherecmd_new:chat,ai-chat,ai-tools,mcp-server,rag,agent,koog,multi-agent,classroom. Each maps 1:1 to a sample incli/samples.json;multi-agentandclassroomare new starters exposing the 5-agent A2A fleet and the AI-classroom Spring Boot + Expo RN sample respectively. create-atmosphere-app(npx) rewritten as a thin delegating shim (944b190f43). Drops the old JBang branch and the 240-line inline Java/HTML fallback, resolves the installedatmosphereCLI on PATH, and execsatmosphere new <name> --template <t> [--skill-file <f>]. Prints an actionable install hint if the CLI is missing.TEMPLATESlist synchronized with the shell CLI's nine entries.
generator/AtmosphereInit.java+AtmosphereInitTest.java+generator/templates/handler/**generator/templates/frontend/**+generator/templates/{Application.java,application.yml,pom.xml}.mustachegenerator/test-generator.sh+.github/workflows/generator-ci.yml(b7f98d42f0). The JBang mustache scaffold is fully gone.generator/ComposeGenerator.javaand itsgenerator/templates/compose/**tree remain — they back the parametric skill-file driven multi-module scaffold invoked byatmosphere compose, which has no single-sample equivalent.
cli/atmospherebash fallback tree —create_minimal_project,create_chat_handler,create_ai_chat_handler,create_agent_handler,create_index_html(~430 lines).cmd_newnow always clones; there is no fallback path.--groupflag onatmosphere newandcreate-atmosphere-app. Samples ship with their own groupId; passing--groupprints a deprecation warning and is ignored. Rename the groupId inpom.xmlandsrc/main/javaby hand after scaffolding if needed.
4.0.36 - 2026-04-13
Every bullet in this section is grounded in a real commit on main at the
time of release; commit hashes are listed where the attribution matters.
- Microsoft Semantic Kernel adapter (
atmosphere-semantic-kernel). SeventhAgentRuntimeimplementation backed by Semantic Kernel'sChatCompletionService. Streams via the SK streaming chat API, honors system prompts, threadsAgentExecutionContextinto the SK invocation context, and reports token usage. Tool calling is deferred in 4.0.36 (SK's JavaKernelFunctiontool binding is not yet bridged throughToolExecutionHelper.executeWithApproval).SemanticKernelEmbeddingRuntimeships alongside for embedding support via SK'sTextEmbeddingGenerationService; blocks the reactive response at a 60s ceiling to avoid pinning a virtual thread on a hung service.
ToolApprovalPolicysealed interface (c83469a478). Four permitted implementations:annotated()(default, honors@RequiresApproval),allowAll()(trusted test fixtures),denyAll()(preview / shadow mode, no invocation ever runs), andcustom(Predicate<ToolDefinition>)for runtime-dependent decisions. Attach viaAgentExecutionContext.withApprovalPolicy(...).ExecutionHandlecooperative cancel for in-flight executions viaAgentRuntime.executeWithHandle(context, session). Idempotentcancel(), terminalwhenDone()future,isDone(). Runtime cancel primitives (verified from source):- Built-in:
HttpClientrequest + SSEInputStream.close() - Spring AI:
reactor.core.Disposable.dispose()on the streamingFlux - LangChain4j:
CompletableFuture.completeExceptionally+AtomicBooleansoft-cancel flag consulted in the streaming response handler - Google ADK:
AdkEventAdapter.cancel()→io.reactivex.rxjava3.disposables.Disposable.dispose()on the Runner subscription - JetBrains Koog:
AtomicReference<Job>captured byexecuteInternal→Job.cancel()+ virtual-threadThread.interrupt()fallback + immediatedone.complete(null)backstop - Semantic Kernel, Embabel: no-op sentinel (
ExecutionHandle.completed()) — neither runtime overridesexecuteWithHandle, documented as a known gap.
- Built-in:
AgentLifecycleListener— observability SPI withonStart,onToolCall,onToolResult,onCompletion,onError. Attach viaAgentExecutionContext.withListeners(List<AgentLifecycleListener>).AbstractAgentRuntimefires start / completion / error via protectedfireStart/fireCompletion/fireErrorhelpers, so the five runtimes that extend it (Built-in, Spring AI, LC4j, ADK, SK) get lifecycle events automatically. Tool events fire through the staticfireToolCall/fireToolResultdispatchers used by every tool bridge. Koog and Embabel implementAgentRuntimedirectly and do not yet fire start / completion / error (documented exclusion indocs/reference/lifecycle-listener.md).EmbeddingRuntimeSPI withfloat[] embed(String),List<float[]> embedAll(List<String>),int dimensions(),isAvailable(),name(),priority(). Five implementations ship; service-loader resolution picks the highest-priority available one at runtime:SpringAiEmbeddingRuntime— priority 200LangChain4jEmbeddingRuntime— priority 190SemanticKernelEmbeddingRuntime— priority 180EmbabelEmbeddingRuntime— priority 170BuiltInEmbeddingRuntime— priority 50 (zero-dep OpenAI-compatible fallback)
- Per-request
RetryPolicyonAgentExecutionContext. Record shape:(int maxRetries, Duration initialDelay, Duration maxDelay, double backoffMultiplier, Set<String> retryableErrors). Only the Built-in runtime currently honors the per-request override; framework runtimes inherit their own native retry layers and the capability is Built-in-only in 4.0.36 (per the pinned capability matrix inAbstractAgentRuntimeContractTest.expectedCapabilities()). - Pipeline-level
ResponseCache(3e1fc6e4a7). SHA-256CacheKeyover model, system prompt, message, response type, conversation history, tool names, and content parts (text / image mime+length / audio mime+length / file mime+length+bytes). Session-ID-independent so identical prompts hit the same cache line.CacheHintmetadata on the context selects policy (CONSERVATIVE,AGGRESSIVE,NONE). - Multi-modal
Content— sealedContenttype withText,Image,Audio,Filesubtypes. Wire frames carry base64-encoded payloads with explicitmimeTypeandcontentType. Runtimes that do not support multi-modal input declare the exclusion in theircapabilities()set (Correctness Invariant #5 — Runtime Truth). session.toolCallDelta()+AiCapability.TOOL_CALL_DELTA— incremental tool-argument streaming so clients can render partial JSON as the model generates it. Declared as anAiCapabilityenum value so the distinction is machine-readable on the SPI, not just prose in the matrix. OnlyBuiltInAgentRuntimeadvertises it — itsOpenAiCompatibleClientforwards everydelta.tool_calls[].function.argumentsfragment throughsession.toolCallDelta(id, chunk)on both the chat-completions and responses-API streaming paths. The six framework bridges (Spring AI, LC4j, ADK, Embabel, Koog, Semantic Kernel) cannot emit deltas without bypassing their high-level streaming APIs (895a7e0a2e); they honor the default no-op contract instead. Pinned in the Built-in contract test and inmodules/integration-tests/e2e/ai-tool-call-delta.spec.ts's negative capability assertion.AgentRuntime.models()default method returning the list of models the resolved runtime can actually serve. Replaces the configuration-intent model flag with a runtime-resolved list (Correctness Invariant #5).TokenUsagerecord(long input, long output, long cachedInput, long total, String model). Reported on completion metadata asai.tokens.input/ai.tokens.output/ai.tokens.total/ai.tokens.cachedwhen the provider surfaces it.@AiEndpoint.promptCache()and@AiEndpoint.retry()— declarative annotations on@AiEndpoint.promptCache()returnsCacheHint.CachePolicy(defaultNONE).retry()returns a nested@Retryannotation withmaxRetries,initialDelayMs,maxDelayMs,backoffMultiplier. Resolved at bean post-processing on Spring Boot and via the annotation processor at build time on Quarkus.AbstractAgentRuntimeContractTest— TCK inmodules/ai-testthat everyAgentRuntimesubclass must pass. Exercises text streaming, tool calling, tool approval, system prompt, multi-modal input, cache hint threading, execution cancel, and capability-set pinning viaexpectedCapabilities()(addedc13e309d) so adding or removing a capability from a runtime without updating its pinned set breaks the build. Drift between the code and the docs matrix intutorial/11-ai-adapters.mdcannot ship silently.
@Agent+@Command— one annotation defines the agent; slash commands routed on every wired channel (Web WebSocket plus the five external channels whenatmosphere-channelsis present). Auto-generates@AiEndpoint, A2A Agent Card, MCP tool manifest, and AG-UI event bindings based on classpath detection.atmosphere-agentmodule — annotation processor,CommandRouter,SkillFileParser,AgentHandler.skill.md— markdown files that serve as both the LLM system prompt and agent metadata (## Skills/## Tools/## Channels/## Guardrailssections parsed into Agent Card, MCP manifest, channel validation).- JetBrains Koog adapter (
atmosphere-koog). SixthAgentRuntimebacked by Koog'sAIAgent/chatAgentStrategy()with tool calling viaAtmosphereToolBridgeand cooperative cancel viaAtomicReference<Job>. - Orchestration primitives — agent handoffs
(
session.handoff(target, message)), approval gates (@RequiresApproval), conditional routing in@Fleet, and LLM-as-judge eval assertions (LlmJudge). - Samples:
spring-boot-dentist-agent,spring-boot-orchestration-demo,spring-boot-checkpoint-agent(durable HITL workflow surviving JVM restart viaSqliteCheckpointStore).
Commit hashes listed for every Fixed bullet. If there is no commit, the bullet does not belong here.
ToolApprovalPolicy.DenyAllbypass — P0 security (40d616b6ee).DenyAll.requiresApproval()previously returnedtrueand fell through to the session-scopedApprovalStrategy, so an auto-approve strategy could silently run a tool the caller intended to deny.ToolExecutionHelper.executeWithApprovalnow detectsDenyAllbefore consulting the strategy and returns{"status":"cancelled","message":"Tool execution denied by policy"}immediately. Closes Correctness Invariant #6 (fail-closed default).- Null-strategy approval bypass (
56b1046f6f). Before the DenyAll evaluation fix, a tool annotated@RequiresApprovalrunning under a context with noApprovalStrategywired would execute unguarded. NowToolExecutionHelperfails closed on null strategy; DenyAll is evaluated before the null-strategy branch. The same commit also stoppedCachingStreamingSessionfrom auto-persisting oncomplete()so a cancel-induced clean termination can no longer cache a partial response — the pipeline decides whether to commit afterruntime.executereturns. ToolApprovalPolicynot threaded through the tool-loop (b9b1af4aff). Every runtime bridge previously called the 5-argexecuteWithApprovaloverload which defaulted toToolApprovalPolicy.annotated()—context.approvalPolicy()was never consumed. All five tool-calling bridges (Built-in, Spring AI, LC4j, ADK, Koog) now call the 6-arg form and pass the policy through.ChatCompletionRequestgainedapprovalPolicyas its 13th canonical field, preserved across tool-loop rounds.tryResolvetri-state approval ID resolution (0db97e3276,c3cc904644).ApprovalRegistry.tryResolve(id)returnedtrueonUNKNOWN_ID, which causedAiPipeline/AiStreamingSession/ChannelAiBridgeto swallow stale or cross-session approval messages as if consumed. Callers now use the tri-stateresolve()method and only short-circuit onRESOLVED, lettingUNKNOWN_IDfall through to the normal pipeline.- Koog cancel race (
ae732f8301).KoogAgentRuntime.executeWithHandlepreviously relied on a soft-cancel flag polled at suspension points, so a cancel racing with a slow KoogPromptExecutorstall could leave the virtual thread hanging on a native I/O read. The runtime now captures the active coroutineJobin anAtomicReference, cancels the job, and interrupts the virtual thread as a belt-and-suspenders fallback. The same commit enables ADKContextCacheConfigbootstrap and fixes anEmbeddingRuntimeResolverstartup-order race. - LC4j premature completion + ADK model override + cross-session approval
fallback removal (
d4c11ca76a). LC4j'sdoExecutenow blocks onhandle.whenDone()before returning so the lifecycle completion fires after the tool stream actually finishes. ADK'sbuildRequestRunnernow honorscontext.model()when set instead of falling through to the module-level default.AiEndpointHandlerno longer performs a cross-session approval fallback, preserving session-ownership guarantees. - LC4j post-cancel error suppression + terminal-reason first-writer wins
(
4ca8e983d8). LC4j now dropsonErrorcallbacks that arrive after the caller cancelled (the underlying HTTP may drain an IOException out of band).ExecutionHandle.Settablenow records the first-writer terminal reason so observers can distinguish cancel from post-cancel error.RetryPolicy.isInheritSentinelformalisesDEFAULT-as-inheritance contract. ResponseCacheobservability gap + structured-output / RAG / guardrail cache-skip (28d381d4ff).CacheKeynow hashesresponseTypeso a structured-JSON request cannot replay a plain-text cached answer.AiPipelineskips the cache when context providers, guardrails, or a latently non-empty tool registry are present. The cache-hit path now firesAgentLifecycleListener.onStart+onCompletionso observability and audit traffic see a clean pair on both hit and miss paths.CacheKeyContent.File collision + tool-loop cache skip (13cf557532).CacheKeynow hashesContent.Fileparts (mime + length, later upgraded to full byte hash inc29542f1e6) so two distinct PDFs of identical length cannot collide.AiPipeline.streamTextskips the cache whencontext.tools()is non-empty so text-only replays never silently drop tool round-trips.CachingStreamingSessionbinary-sendContent poisoning (0670e2f8b3). BinaryContent(Image / Audio / File) cannot ride through a text-onlyStringBuilder, so the defaultsendContentthrow would have bypassed the text capture. Override now marks the session as errored so the pipeline's post-execute commit short-circuits — never caching a partial text-only response for a flow that emitted binary output.- Embedding runtime timeout + resolver DCL + cancel-swallow logging
(
c29542f1e6).SemanticKernelEmbeddingRuntime.block()calls inherit a 60s ceiling so a hung service cannot pin a virtual thread forever.EmbeddingRuntimeResolverwraps the slow path in a synchronized block so two early callers do not race duplicate ServiceLoader scans.ExecutionHandle.Settable.cancellogs native-cancel exceptions at TRACE instead of silently swallowing them, and documents the first-writer terminal-reason race explicitly. - Release automation stale-version patterns (
a8516e9d7c). Two gaps inscripts/update-doc-versions.shleftREADME.md"Current release" andcli/sdkman/*.mdpublish.shexamples pointing at the previous release afterrelease-4x.ymlran. Both patterns are now swept on every release. - Cross-repo docs sync on release (
dce1fba280,aadec4e1d8).release-4x.ymlnow fires arepository_dispatchevent atAtmosphere/atmosphere.github.iovia the existingSITE_DISPATCH_TOKENon successful release. A companionsync-version.ymlworkflow in the docs repo runsscripts/update-doc-versions.shand commits the result. Closes the long-standing gap where the docs site lagged the Maven Central release by days.
4.0.11 - 2026-03-11
- WebSocket XSS sanitization bypass. Disabled HTML sanitization for WebSocket transport — HTML-encoding JSON in WebSocket frames broke the AI streaming wire protocol.
- XSS and insecure cookie hardening. Sanitize HTML output in write
methods and set the
Secureflag on cookies over HTTPS.
- Token → Streaming Text rename. All AI module APIs, javadoc,
and the atmosphere.js client now use "streaming text" instead of "token"
to describe LLM output chunks. This affects method names
(
onToken→onStreamingText,totalTokens→totalStreamingTexts), field names, and the wire protocol message type ("token"→"streaming-text"). This is a breaking change for atmosphere.js consumers and customAiStreamBroadcastFilterimplementations. - Javadoc published to GitHub Pages. API docs for
atmosphere-runtimeare now deployed automatically toasync-io.org/apidocs. - Starlight tutorial site. A 20-chapter tutorial book is now available at the project documentation site.
4.0.3 - 2026-02-22
- Room Protocol broadcast bug.
DefaultRoom.broadcast()now wraps messages inRawMessageto bypass@Messagedecoder mangling. Room JSON envelopes (join/leave/message events) are delivered intact to clients. enableHistory()NPE.UUIDBroadcasterCacheis now properly configured before use, preventingNullPointerExceptionwhen room history is enabled.- Native Image build. Spring Boot samples use
process-aotandexecclassifier in thenativeprofile so GraalVM can find the main class.
RawMessageAPI (org.atmosphere.cpr.RawMessage) — first-class public wrapper for pre-encoded messages that bypass@Messagedecoder/encoder pipelines.ManagedAtmosphereHandler.Managedis deprecated in favor ofRawMessage.- Playwright E2E tests for all sample applications (chat, spring-boot-chat, embedded-jetty, quarkus-chat, AI samples, durable-sessions, MCP server).
- Unified parent POM. All samples now inherit from
atmosphere-project, makingmvn versions:setupdate every module in a single command. - Normalized artifact names. All modules use lowercase kebab-case
atmosphere-*naming consistently. - Release workflow hardened. Stale tags are cleaned before tagging, and
git rebasehandles diverged branches during release builds.
4.0.0 - 2026-02-18
Atmosphere 4.0 is a rewrite of the framework for JDK 21+ and Jakarta EE 10. It keeps the annotation-driven programming model and transport abstraction from prior versions, and adds support for virtual threads, AI/LLM streaming, rooms and presence, native image compilation, and frontend framework bindings.
This release succeeds the 2.x/3.x line (last release: 3.1.0 / 2.7.16). The
javax.servlet namespace, Java 8 runtime, and legacy application server
integrations have been removed. Applications migrating from 2.x or 3.x should
consult the Migration Guide.
- JDK 21 minimum requirement. The framework compiles with
--release 21and is tested on JDK 21, 23, and 25 in CI. - Jakarta EE 10 baseline. All Servlet, WebSocket, and CDI APIs use the
jakarta.*namespace. Servlet 6.0, WebSocket 2.1, and CDI 4.0 are the minimum supported versions. - Virtual Thread support.
ExecutorsFactorycreates virtual-thread-per-task executors by default viaExecutors.newVirtualThreadPerTaskExecutor().DefaultBroadcasterand 16 other core classes have been migrated fromsynchronizedblocks toReentrantLockto avoid virtual thread pinning. Virtual threads can be disabled withApplicationConfig.USE_VIRTUAL_THREADS=false. - GraalVM native image support. Both the Spring Boot starter and Quarkus extension include reflection and resource hints for ahead-of-time compilation. Spring Boot requires GraalVM 25+; Quarkus works with GraalVM 21+ or Mandrel.
atmosphere-spring-boot-starter-- Spring Boot 4.0 auto-configuration with annotation scanning, Spring DI bridge (SpringAtmosphereObjectFactory), Actuator health indicator (AtmosphereHealthIndicator), and GraalVM AOT runtime hints (AtmosphereRuntimeHints). Configuration viaatmosphere.*properties inapplication.yml.atmosphere-quarkus-extension(runtime + deployment) -- Quarkus 3.21+ extension with build-time Jandex annotation scanning, Arc CDI integration, customQuarkusJSR356AsyncSupport, and@BuildStep-driven native image registration. Configuration viaquarkus.atmosphere.*properties.atmosphere-ai-- AI/LLM streaming SPI. DefinesStreamingSession,StreamingSessions,AiStreamingAdapter, andAiConfigfor streaming streaming texts from any LLM provider to connected clients. Includes the@AiEndpointannotation for zero-boilerplate AI handlers and the@Promptannotation for marking prompt-handling methods that run on virtual threads automatically.atmosphere-spring-ai-- Spring AI adapter (SpringAiStreamingAdapter) that bridgesChatClientstreaming responses toStreamingSession.atmosphere-langchain4j-- LangChain4j adapter (LangChain4jStreamingAdapter,AtmosphereStreamingResponseHandler) for callback-based LLM streaming.atmosphere-embabel-- Embabel Agent Framework adapter for agentic AI with progress events.atmosphere-mcp-- Model Context Protocol (MCP) server module. Annotation-driven tools (@McpTool), resources (@McpResource), prompts (@McpPrompt), and server declaration (@McpServer). Supports WebSocket transport, Streamable HTTP transport (MCP 2025-03-26 spec), stdio bridge for Claude Desktop, and a programmaticMcpRegistryAPI.atmosphere-kotlin-- Kotlin DSL (atmosphere { ... }builder) and coroutine extensions (broadcastSuspend,writeSuspend) for idiomatic Kotlin integration. Requires Kotlin 2.1+.atmosphere-redis-- Redis clustering broadcaster using Lettuce 6.x for non-blocking pub/sub. Messages broadcast on any node are delivered to clients connected to all other nodes.atmosphere-kafka-- Kafka clustering broadcaster using the Apache Kafka client 3.x. Configurable topic prefix, consumer group, and bootstrap servers.atmosphere-durable-sessions-- Durable session SPI withDurableSessionInterceptor,SessionStoreinterface, and in-memory implementation. Sessions survive server restarts; room memberships, broadcaster subscriptions, and metadata are restored on reconnection.atmosphere-durable-sessions-sqlite-- SQLite-backedSessionStorefor single-node deployments.atmosphere-durable-sessions-redis-- Redis-backedSessionStorefor clustered deployments.atmosphere-integration-tests-- Integration test suite with embedded Jetty and Testcontainers covering WebSocket, SSE, long-polling transports, Redis and Kafka clustering, and MCP protocol compliance.
- Room API (
org.atmosphere.room).RoomManagercreates and manages named rooms backed by dedicatedBroadcasterinstances.Roomsupportsjoin,leave,broadcast, presence tracking viaonPresencecallbacks, and configurable message history replay for late joiners. - Room protocol (
org.atmosphere.room.protocol).RoomProtocolMessageis a sealed interface withJoin,Leave,Broadcast, andDirectrecord subtypes, enabling exhaustive pattern matching in Java 21 switch expressions. @RoomServiceannotation for declarative room handler registration with automaticRoomcreation viaRoomManager.VirtualRoomMemberfor adding LLM agents as room participants.- Room authorization (
RoomAuth,RoomAuthorizer) for controlling room access. RoomProtocolInterceptorfor automatic protocol message parsing and dispatching.
- Micrometer metrics (
AtmosphereMetrics). Registers gauges, counters, and timers on anAtmosphereFrameworkinstance: active connections, active broadcasters, total connections, messages broadcast, broadcast latency, room-level gauges, cache hit/miss/eviction counters, and backpressure drop/disconnect metrics. Requiresmicrometer-coreon the classpath (optional dependency). - OpenTelemetry tracing (
AtmosphereTracing). Interceptor that creates spans for every request lifecycle with attributes:atmosphere.resource.uuid,atmosphere.transport,atmosphere.action,atmosphere.broadcaster,atmosphere.room. Requiresopentelemetry-apion the classpath (optional dependency). - Health check (
AtmosphereHealth). Framework-level health snapshot reporting status, version, active connections, and broadcaster count. Integrated into the Spring Boot Actuator health endpoint viaAtmosphereHealthIndicator. - MDC interceptor (
MDCInterceptor). Setsatmosphere.uuid,atmosphere.transport, andatmosphere.broadcasterin the SLF4J MDC for structured logging.
BackpressureInterceptor-- protects against slow clients with configurable high-water mark (default 1000 pending messages) and overflow policies:drop-oldest,drop-newest, ordisconnect.
- atmosphere.js 5.0 -- TypeScript rewrite with no runtime dependencies. Ships as ESM, CJS, and IIFE bundles.
- Transport fallback -- WebSocket with configurable fallback to SSE, HTTP streaming, or long-polling. Full protocol handler with heartbeat, reconnection, and message tracking.
- React hooks --
useAtmosphere,useRoom,usePresence,useStreamingviaatmosphere.js/react. IncludesAtmosphereProviderfor connection lifecycle management. - Vue composables --
useAtmosphere,useRoom,usePresence,useStreamingviaatmosphere.js/vue. - Svelte stores --
createAtmosphereStore,createRoomStore,createPresenceStore,createStreamingStoreviaatmosphere.js/svelte. - AI streaming client --
subscribeStreamingwithonStreamingText,onProgress,onComplete, andonErrorcallbacks for real-time LLM streaming text display. - Room and presence client API -- join/leave rooms, broadcast within rooms, track online members, and display presence counts.
- Chat UI components -- shared React chat components for sample
applications via
atmosphere.js/chat.
spring-boot-chat-- Spring Boot 4 chat application with React frontend.quarkus-chat-- Quarkus 3.21+ chat application.chat-- Standalone Jetty embedded chat.embedded-jetty-websocket-chat-- Embedded Jetty with WebSocket.grpc-chat-- Standalone gRPC transport chat.spring-boot-ai-chat-- Streaming AI chat via theAgentRuntimeSPI.spring-boot-ai-tools-- Portable@AiTooltool calling across runtimes.spring-boot-ai-classroom-- Multi-room AI with a React Native / Expo client.spring-boot-rag-chat-- RAG chat withContextProvider.spring-boot-mcp-server-- MCP server with annotation-driven tools.spring-boot-durable-sessions-- Durable sessions with SQLite backend.
- Multi-JDK CI -- GitHub Actions matrix testing on JDK 21, 23, and 25.
- Native image CI -- GraalVM native builds for both Spring Boot and Quarkus with smoke tests.
- atmosphere.js CI -- TypeScript build, test, lint, and bundle size verification.
- Samples CI -- Compilation verification for all sample applications including frontend npm builds.
- Unified release workflow (
release-4x.yml) for coordinated Maven Central and npm publishing. - CodeQL analysis for automated security scanning.
- Pre-commit hooks enforcing Apache 2.0 copyright headers and conventional commit message format.
- Checkstyle and PMD enforced in the
validatephase withfailsOnError=true.
- Java 8 minimum raised to Java 21. All source compiled with
--release 21. javax.servletnamespace replaced withjakarta.servletthroughout the codebase.- Jetty 9 support replaced with Jetty 12 (
12.0.16). - Tomcat 8 support replaced with Tomcat 11 (
11.0.18). - SLF4J upgraded from 1.x to 2.0.16; Logback from 1.2.x to 1.5.18.
synchronizedblocks inDefaultBroadcaster,AtmosphereResourceImpl,AsynchronousProcessor, and 13 other core classes replaced withReentrantLockfor virtual thread compatibility.HashMapandArrayListin concurrent contexts replaced withConcurrentHashMapandCopyOnWriteArrayList.ScheduledExecutorServiceremains on platform threads for timed tasks (expected -- virtual threads do not benefit from scheduling).
instanceofchecks replaced with pattern matching throughout the codebase.if/elsechains on enums replaced with switch expressions (JDK 21).- Immutable collection factories (
List.of(),Map.of(),Set.of()) used in place ofCollections.unmodifiable*wrappers. - Lambda expressions replace anonymous inner classes where appropriate.
String.repeat()replaces manual loop concatenation.- Diamond operator applied consistently.
try-with-resourcesapplied to allAutoCloseableusage.varused for local variables where the type is obvious from context.- Records used for room protocol messages (
Join,Leave,Broadcast,Direct), cache entries, and event types. - Sealed interfaces used for
RoomProtocolMessageand related type hierarchies.
- atmosphere.js rewritten from jQuery-based JavaScript to TypeScript with zero runtime dependencies.
- Package renamed to
atmosphere.json npm, version 5.0.0. - Build tooling changed from Grunt/Bower to tsup (esbuild-based bundler) with Vitest for testing.
- Module format changed from AMD/global to ESM + CJS + IIFE triple output.
- Peer dependencies on React 18+, Vue 3.3+, and Svelte 4+ are all optional.
- TestNG retained for core
atmosphere-runtimetests. - JUnit 5 adopted for Spring Boot starter tests (via
spring-boot-starter-test). - JUnit 5 adopted for Quarkus extension tests (via
quarkus-junit5). - Mockito upgraded to 5.21.0 for JDK 25 compatibility (ByteBuddy 1.17.7).
- Integration tests use Testcontainers for Redis and Kafka.
JSR356WebSocketTestexcluded (Mockito cannot mock sealed interfaces on JDK 21+).
AtmosphereFrameworkdecomposed into focused component classes. The former 3,400-line god object is now an orchestrator (~2,260 lines) that delegates to single-responsibility components. The public API is fully preserved -- all existingframework.addAtmosphereHandler(),framework.interceptor(), etc. calls continue to work unchanged. New internal components:BroadcasterSetup-- broadcaster configuration, factory, and lifecycleClasspathScanner-- annotation scanning, handler/WebSocket auto-detectionInterceptorRegistry-- interceptor lifecycle and orderingHandlerRegistry-- handler registration and endpoint mappingWebSocketConfig-- WebSocket protocol and processor configurationFrameworkEventDispatcher-- listener management and lifecycle eventsFrameworkDiagnostics-- startup diagnostics and analytics reporting
AtmosphereHandlerWrapperfields encapsulated. Previously public mutable fields (broadcaster,interceptors,mapping) are now private with accessor methods.- Inner classes promoted to top-level.
AtmosphereHandlerWrapper,MetaServiceAction, andDefaultAtmosphereObjectFactoryare now standalone classes inorg.atmosphere.cpr.
- Legacy Maven repositories (Codehaus, maven.java.net, JBoss Nexus, Sonatype) removed. All dependencies sourced from Maven Central.
- Publishing migrated from legacy OSSRH to the Central Publishing
Portal (
central-publishing-maven-plugin). - CDDL-licensed Jersey utility classes (
UriTemplate,PathTemplate) replaced with Apache 2.0 implementations. - OSGi bundle configuration updated for
jakarta.*imports.
- Java 8, 11, and 17 support. JDK 21 is the minimum.
javax.servletnamespace. All APIs usejakarta.*.- Legacy application server support. GlassFish 3/4, Jetty 6-9, Tomcat 6-8, WebLogic, JBoss AS 7, and Netty-based transports are no longer supported. The framework targets Servlet 6.0+ containers (Jetty 12, Tomcat 11, Undertow via Quarkus).
- Deprecated APIs. Two passes of deprecated code removal
(
cf24377f0,a8e6f2be3) cleaned out dead code paths, unused configuration options, and obsolete utility classes accumulated over the 2.x/3.x lifecycle. - CDDL-licensed code. Jersey-derived
UriTemplateand related classes removed and replaced with Apache 2.0 implementations. - jQuery dependency in atmosphere.js. The client library has zero runtime dependencies.
- Netty, Play Framework, and Vert.x integrations. These have been moved to a legacy section and are no longer maintained.
- Update your JDK. Atmosphere 4.0 requires JDK 21 or later.
- Replace
javax.servletimports withjakarta.servlet. This includesHttpServletRequest,HttpServletResponse,ServletContext, and all related types. - Update your container. Use Jetty 12+, Tomcat 11+, or deploy via Spring Boot 4.0+ / Quarkus 3.21+.
- Review synchronized code. If you extended core Atmosphere classes
that used
synchronized, your subclasses may need correspondingReentrantLockupdates. - Check deprecated API usage. Methods and classes deprecated in 2.x and 3.x have been removed. Consult the Javadoc for replacements.
- Remove jQuery. atmosphere.js 5.0 has no jQuery dependency.
- Update imports. The package is now
atmosphere.json npm. Useimport { atmosphere } from 'atmosphere.js'. - Review transport configuration. The new client supports the same transports (WebSocket, SSE, long-polling, streaming) but the configuration API has been streamlined.
| Module | GroupId | ArtifactId | Version |
|---|---|---|---|
| Core runtime | org.atmosphere |
atmosphere-runtime |
4.0.0 |
| Spring Boot starter | org.atmosphere |
atmosphere-spring-boot-starter |
4.0.0 |
| Quarkus extension | org.atmosphere |
atmosphere-quarkus-extension |
4.0.0 |
| AI streaming SPI | org.atmosphere |
atmosphere-ai |
4.0.0 |
| Spring AI adapter | org.atmosphere |
atmosphere-spring-ai |
4.0.0 |
| LangChain4j adapter | org.atmosphere |
atmosphere-langchain4j |
4.0.0 |
| Embabel adapter | org.atmosphere |
atmosphere-embabel |
not yet published (pending Embabel Maven Central release) |
| MCP server | org.atmosphere |
atmosphere-mcp |
4.0.0 |
| Kotlin DSL | org.atmosphere |
atmosphere-kotlin |
4.0.0 |
| Redis clustering | org.atmosphere |
atmosphere-redis |
4.0.0 |
| Kafka clustering | org.atmosphere |
atmosphere-kafka |
4.0.0 |
| Durable sessions | org.atmosphere |
atmosphere-durable-sessions |
4.0.0 |
| Durable sessions (SQLite) | org.atmosphere |
atmosphere-durable-sessions-sqlite |
4.0.0 |
| Durable sessions (Redis) | org.atmosphere |
atmosphere-durable-sessions-redis |
4.0.0 |
| TypeScript client | atmosphere.js (npm) |
atmosphere.js |
5.0.0 |
| Dependency | Minimum Version | Tested Up To |
|---|---|---|
| JDK | 21 | 25 |
| Servlet API | 6.0 (Jakarta EE 10) | 6.1 |
| Spring Boot | 4.0.5 | 4.0.5 |
| Spring Framework | 6.2.8 | 6.2.8 |
| Quarkus | 3.21 | 3.31.3 |
| Jetty | 12.0 | 12.0.16 |
| Tomcat | 11.0 | 11.0.18 |
| Kotlin | 2.1 | 2.1+ |
| GraalVM (Spring Boot) | 25 | 25 |
| GraalVM / Mandrel (Quarkus) | 21 | 25 |
For changes in the 2.x and 3.x release lines, see the
GitHub Releases page
and the atmosphere-2.6.x branch.