This is an automated email from the ASF dual-hosted git repository.

pjfanning pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko.git


The following commit(s) were added to refs/heads/main by this push:
     new c9cba616eb Add a draft threat model and wire it for discoverability 
(#3478)
c9cba616eb is described below

commit c9cba616ebcd700c266424ff5403735c735477ed
Author: Jarek Potiuk <[email protected]>
AuthorDate: Mon Sep 7 19:12:13 2026 +0200

    Add a draft threat model and wire it for discoverability (#3478)
    
    * Add a draft threat model and wire it for discoverability
    
    ### Motivation
    The Pekko PMC asked the ASF Security team for a draft threat model to 
refine,
    as preparation for a Claude security scan. Pekko documents its security 
posture
    well in remote-security.md, serialization.md and remoting.md, but that 
material
    is spread across the docs and states the trust model implicitly rather than 
in a
    form a triager (or a scanning agent) can cite when classifying a finding.
    
    Separately, the AGENTS.md -> SECURITY.md chain a scanning agent follows to 
locate
    a project's model does not currently resolve: there is no SECURITY.md in 
the repo.
    
    ### Modification
    - THREAT_MODEL.md: a v0 draft following the Scovetta rubric. Every claim is 
tagged
      (documented) with a citation, or (inferred) with a matching open question 
in §14.
      40 documented / 0 maintainer / 14 inferred.
    - SECURITY.md: reporting policy pointing at [email protected], plus a 
link to
      the threat model and the two assumptions that catch most reporters.
    - AGENTS.md: a Security section pointing at both.
    
    Nothing the existing docs assert has been dropped or weakened; §15 is a 
back-map
    from each existing statement to the section that now carries it.
    
    ### Result
    The discovery chain AGENTS.md -> SECURITY.md -> THREAT_MODEL.md resolves on 
main,
    and triagers have a citable document with a closed set of dispositions 
(§13).
    
    §14 holds 13 questions for the PMC, each stated as a proposed answer to 
confirm or
    correct. The highest-value ones are Q1 (is the plaintext `transport = tcp` 
default
    the supported production posture, given the documented network-isolation
    assumption?) and Q4 (are untrusted-mode and the deployment allow list 
security
    boundaries, or hardening?). The document is not ready to be treated as 
canonical
    until those are answered.
    
    ### Tests
    Not run - docs only
    
    ### References
    None - requested by the Pekko PMC chair on the ASF security scan thread
    
    * docs: correct scope and citations in the draft threat model
    
    ### Motivation
    
    Review of the v0 draft found the component-scope tables understate Pekko's
    network surface, and two cited claims are documented only on the 2.0 line.
    
    ### Modification
    
    - §2/§4/§5/§6 - `pekko-actor` ships `org.apache.pekko.io.Tcp`/`Udp`/`Dns` 
and
      `pekko-stream` ships `Tcp`, `TLS`, `FileIO`, `Framing`, so the "actor 
core is
      purely in-process" claim and the "streams leave the process only via
      connectors the app supplies" row were both wrong. Added the matching input
      rows so a framing defect routes to a §13 disposition.
    - §2 - added `protobuf-v3`; listed `plugins`, `bill-of-materials` and
      `scala-nightly` as out of scope.
    - §5a - added the classic-remoting knobs, which are deprecated but shipped 
and
      CI-gated, and were previously absent.
    - §5a/§15 - `serialize-messages` is documented as test-only in 
reference.conf,
      so Q13 is answered; reused the slot for classic remoting's scope.
    - §8 P7 and §9 - flagged the cert-rotation and service-mesh citations as 
2.0+
      and linked the snapshot docs, since neither is in the released 1.x docs.
    - §1 - added the heading §15 already back-mapped to.
    - SECURITY.md - `remote-security.html` 404s on `current`; pointed at
      `remoting-artery.html#remote-security`, which resolves on both lines.
    - security/index.md - named `[email protected]` to match SECURITY.md, and
      linked both new documents so the docs site closes the same chain.
    
    ### Result
    
    Scope tables match the code, every citation resolves for the version it 
claims,
    and §14 still holds 13 questions.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * Update SECURITY.md
    
    * docs: drop the app-bound socket scope ruling from the threat model
    
    ### Motivation
    
    The previous commit corrected §2/§5 to record that `pekko-actor` ships
    `org.apache.pekko.io.Tcp`/`Udp` and `pekko-stream` ships `Tcp`, `TLS`, 
`FileIO`
    and `Framing`. It also added a §4 sentence declaring parsing and framing
    defects reachable from an application-bound socket to be in model. That 
second
    part is a scope ruling, not a factual correction, and it was neither tagged
    `(inferred)` nor raised as a §14 question, so it entered the document 
without
    maintainer sign-off.
    
    It was also too broad: it asserted such bytes are attacker-controlled when 
the
    application may bind localhost, it did not distinguish Pekko's transport
    plumbing from the application's own protocol, and it swept in `stream.TLS`,
    which delegates to the JDK's JSSE.
    
    ### Modification
    
    - §4 - removed the exception sentence.
    - §6 - the application-bound socket rows now describe the inputs without
      asserting an obligation on Pekko; attacker-controllability is stated as
      conditional on where the application exposes the socket.
    
    ### Result
    
    §2, §5 and §6 describe the surface accurately, and §4 makes no scope claim
    about it either way. Whether defects in Pekko's own transport and framing 
code
    are in model is left open for the maintainers.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: keep the original upstream-coordination wording in SECURITY.md
    
    ### Motivation
    
    An earlier review commit rewrote the upstream-coordination paragraph on the
    assumption that the draft had dropped a statement from the docs. It had not:
    `docs/src/main/paradox/security/index.md` on `main` already carries the same
    generic wording, so the draft was faithful to it and the rewrite was
    unnecessary. Restoring the sentence alongside the rewrite left the same 
claim
    stated twice in consecutive paragraphs.
    
    ### Modification
    
    Removed the added paragraph, leaving the original sentence in place.
    `SECURITY.md` now differs from the draft only in the Remote Security link,
    which pointed at a page that does not exist on the released docs.
    
    ### Result
    
    No duplicated statement, and the file matches the wording already published 
on
    the documentation site.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: record the maintainer security posture and answer Q1, Q3 and Q4
    
    ### Motivation
    
    The draft left the §5a defaults marked UNRESOLVED and carried no maintainer
    provenance at all. Pekko's position is that it is a hardening toolkit rather
    than a secure-by-default one: its deployment base is inherited from Akka, 
and
    operators there have already been tasked with securing the network Pekko 
runs
    on and controlling who may reach a deployed system. Tightening a default 
breaks
    those deployments on upgrade.
    
    That position needs stating, because the rest of the model depends on it and
    because a threat model claiming Pekko is secure by default would not survive
    contact with `reference.conf`.
    
    ### Modification
    
    - **§5b** - new section stating the posture: defaults are compatibility
      choices, not security claims; a request to tighten one is a change request
      rather than a vulnerability report; such proposals are welcome on the
      development list and weighed in good faith; and a control that does not do
      what it documents once enabled is a defect Pekko fixes.
    - **§13** - new `BY-DESIGN: default-configuration` disposition, which 
closes a
      report while pointing at the route to propose the change.
    - **§14** - Q1, Q3 and Q4 answered and retained in place so cross-references
      still resolve. Two proposed dispositions are corrected as they did not fit
      §13's own definitions: Q1 routed an unassociated attacker to
      `adversary-not-in-scope`, which §13 reserves for an associated peer, a PKI
      certificate or in-JVM execution, and which §7 contradicts by listing that
      attacker as in scope; Q4 routed a bypass of an enabled control to
      `VALID-HARDENING`, which §13 defines as violating no §8 property, though 
such
      a bypass violates P5 or P4.
    - **§5a, §7, §9, §11a** - stance cells filled in and the claims that were
      inferred pending these questions retagged `(maintainer)`.
    
    ### Result
    
    Provenance moves from 42/0/14 to 41/12/9, the §5a table has no unresolved
    cells, and §14 holds ten open questions rather than thirteen.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: answer Q2 and record who owns gadget-chain defence
    
    ### Motivation
    
    Q2 asked how a deserialization finding that requires
    `allow-java-serialization = on` should be triaged. The draft proposed
    `OUT-OF-MODEL: non-default-build` but left unstated what an operator who
    enables the flag is then responsible for, which matters because Pekko 
provides
    nothing to help them: `JavaSerializer.fromBinary` performs an unfiltered
    `ObjectInputStream.readObject` and there is no `ObjectInputFilter` 
integration
    anywhere in the project.
    
    ### Modification
    
    - **§14 Q2** - answered. Findings requiring the flag are out of model,
      gadget chains included. Two carve-outs are stated explicitly, per §5b.4: a
      serious defect in Pekko's own serialization implementation remains in 
scope
      and may warrant a CVE, and Java deserialization occurring despite the flag
      being `off` violates §8 P1 and is valid at Critical.
    - **§9** - records that Pekko integrates no serialization filter, cites the
      call site, and names the JVM-level levers an operator has instead.
    - **§10.3** - the operator responsibility now says to maintain a 
gadget-chain
      allow list via `-Djdk.serialFilter` or a process-wide `ObjectInputFilter`.
    - **§5a** - stance cell updated.
    
    ### Result
    
    §14 holds nine open questions. Provenance moves to 41 documented / 16
    maintainer / 9 inferred.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: answer Q5 - the persistence store is trusted
    
    ### Motivation
    
    Q5 asked whether replay should treat journal and snapshot bytes as 
untrusted.
    It should not. Securing the database or persistence store belongs to whoever
    administers it, and Pekko does not take responsibility for treating 
persisted
    values as potentially compromised.
    
    ### Modification
    
    - **§14 Q5** - answered. A finding whose precondition is write access to the
      journal or snapshot store is `OUT-OF-MODEL: trusted-input`. The answer is
      scoped to the store: a defect in Pekko's own replay handling stays in 
scope
      per §5b.4.
    - **§4, §6, §9** - the storage-trust question is replaced by the ruling.
    - **§13** - journal and snapshot contents named in the `trusted-input`
      disposition.
    - **§10** - new item 9, since §5b makes this list the operator contract and 
it
      carried nothing about the persistence store.
    
    Also corrected a cross-reference: §4's PKI bullet cited §14 Q5, which is the
    persistence question. PKI material is stated as trusted in §6.
    
    ### Result
    
    §14 holds eight open questions. Provenance moves to 41 documented / 21
    maintainer / 9 inferred.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: answer the osgi half of Q6 - in model, but a removal candidate
    
    ### Motivation
    
    Q6 asked two things about the §2 module split: whether `kubernetes/` is
    supported code, and whether `osgi` should stay in model given its 
classloading
    surface. The `osgi` half is settled: it is a barely used feature and the 
project
    may eventually remove it rather than carry the maintenance overhead, but 
while
    it ships, security reports against it are accepted.
    
    ### Modification
    
    - **§14 Q6** - marked partly answered. `osgi` stays in model and a finding
      there is not `OUT-OF-MODEL: unsupported-component`. The `kubernetes/`
      question and the rest of the split remain open.
    - **§2** - the `osgi` row and the table footnote record the ruling.
    - **§12** - the list of conditions that would change the model covered only
      promotion of a module into the supported surface. Removal is now named 
too,
      since `osgi` is a candidate and dropping it would change how findings 
there
      are triaged.
    
    ### Result
    
    §14 holds seven fully open questions and one partly answered. Provenance 
moves
    to 41 documented / 23 maintainer / 10 inferred.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: scope the model to this repository and finish Q6
    
    ### Motivation
    
    Q6's remaining half asked whether `kubernetes/` is supported code. It is 
not a
    module at all: four files that provision a GKE test cluster, with no build
    definition. The Kubernetes functionality people would report against -
    discovery, bootstrap, lease - is not in this repository; it ships from 
Apache
    Pekko Management.
    
    That exposed a larger gap. Nothing in the document said which repository it
    covers. Outside the header's "core toolkit", a triager had no way to tell 
where
    a Pekko Management or Pekko HTTP finding belongs, and §13 offered no routing
    for one.
    
    ### Modification
    
    - **§14 Q6** - answered. `kubernetes/` stays out of scope as build tooling, 
and
      the Kubernetes implementation is recorded as living in a separate 
deliverable.
    - **§3** - new non-goal stating that this document covers `apache/pekko` 
only.
      Add-on deliverables are named, with the routing made explicit: out of 
model
      *for this document* is not a dismissal, reports go to the same address, 
and
      they are triaged on the same terms - an implementation defect is Pekko's 
to
      fix, a request to change a default is a §5b change request.
    - **§2** - the build row says what `kubernetes/` actually contains.
    
    ### Result
    
    §14 holds seven open questions. Provenance moves to 41 documented / 26
    maintainer / 10 inferred.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: correct how findings in other Pekko repositories are routed
    
    ### Motivation
    
    The previous commit sent a finding in another Apache Pekko repository to
    `OUT-OF-MODEL: unsupported-component`. That is wrong. §13 defines that
    disposition as landing in a §3 component - test kits, benchmarks, build
    tooling - and applying it to code that simply ships from a different
    repository reads as a dismissal of a report that is in fact accepted
    normally.
    
    Pekko Management issues are reported the same way core issues are. The
    repository differs; the process does not.
    
    ### Modification
    
    Reworded the §3 entry so it limits coverage rather than standing: this
    document enumerates the core toolkit's surfaces and does not enumerate the
    other deliverables', but reporting, triage, the address and the §5b posture 
are
    identical across all of them. States explicitly that such a finding is not
    `OUT-OF-MODEL: unsupported-component`, so the disposition is not misapplied.
    §14 Q6 follows the same correction.
    
    ### Result
    
    No disposition implies a report is out of scope purely because of which 
Pekko
    repository it lands in.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: answer Q8, and Q7's environment-variable claim
    
    ### Motivation
    
    Q7's §5 negative claims were written as bare assertions with nothing a 
reviewer
    could check, and one of the five had already proved wrong. Q8 asked for a
    categorical line on resource growth driven by a remote peer.
    
    ### Modification
    
    **Q8 - answered.** Super-linear in message size is a bug and is valid;
    constant-factor is not. Two caveats recorded: artery already bounds message 
size
    by configuration, so the rule's input is bounded on the remoting path; and 
the
    rule speaks to size, leaving exhaustion driven by message volume from an
    associated peer subject to §7.
    
    **Q7 - partly answered.** Environment variables are read during 
configuration
    startup and never modified. Pekko calls neither `System.getenv` nor 
`sys.env`
    anywhere in the main sources; values arrive only through HOCON substitution 
when
    `ConfigFactory.load` resolves the configuration, which is the documented 
way to
    supply passwords.
    
    The remaining §5 claims now carry citations, and two were narrower than 
written:
    `CoordinatedShutdown` registers a JVM shutdown hook, and `StandardOutLogger`
    prints to stdout during early startup, bounded by `pekko.stdout-loglevel`.
    Verified absent from the main sources: `sun.misc.Signal`, `SignalHandler`,
    `Runtime.exec`, `ProcessBuilder`, `System.setProperty`, `Locale.setDefault`,
    `TimeZone.setDefault`.
    
    ### Result
    
    §14 holds five open questions. Provenance moves to 41 documented / 29
    maintainer / 10 inferred.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: answer Q9 - no compromised-node detection anywhere
    
    ### Motivation
    
    Q9 asked whether §7's no-Byzantine-peer conclusion, reached from the 
remoting
    documentation, generalises to cluster membership, sharding, singleton and
    `distributed-data`. It does, and for a stronger reason than the draft gave:
    Pekko has no guarantee of being able to recognise a compromised node at all.
    
    ### Modification
    
    - **§14 Q9** - answered. No subsystem claims resilience against a 
misbehaving
      member. A finding whose precondition is "a cluster member misbehaves" is
      `OUT-OF-MODEL: adversary-not-in-scope`, and so is one that assumes Pekko
      should have detected the compromise.
    - **§7** - the generalisation is now stated rather than inferred, with the
      reason: failure detection is heartbeat-based
      (`PhiAccrualFailureDetector.scala`), so it identifies members that stop
      responding, not members that respond dishonestly. A compromised node that
      keeps heartbeating is indistinguishable from a healthy one.
    - **§9** - the property is widened from "no Byzantine fault tolerance" to
      include the absence of any compromised-node detection, which is the part a
      reporter is more likely to assume exists.
    
    ### Result
    
    §14 holds four open questions. Provenance moves to 41 documented / 32
    maintainer / 9 inferred.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: answer Q7, Q10 and Q11
    
    ### Motivation
    
    Q7's remaining claims now each carry a code citation, so confirming them is 
a
    read rather than an investigation. Q10 and Q11 were policy questions with no
    code dependency.
    
    ### Modification
    
    - **Q7 - answered.** No signal handlers, child processes or process-global
      mutation, verified absent from the main sources. Two claims stand narrower
      than first written and say so in §5: `CoordinatedShutdown` registers one 
JVM
      shutdown hook, and `StandardOutLogger` prints to stdout during early 
startup
      bounded by `pekko.stdout-loglevel`.
    - **Q10 - answered.** Records which of the three security documents is
      canonical for what, and that the other two link to this one for scope 
rather
      than restating it. Carries a maintenance note: the reporting wording is
      duplicated between `SECURITY.md` and `security/index.md` and has already
      drifted once, so until one becomes a pointer to the other, both must be
      changed together.
    - **Q11 - answered.** The non-goals are confirmed. Two clarifications added 
to
      the draft's wording: the side-channel disclaimer is scoped to Pekko's own
      guarantees, since TLS is delegated to the JDK's JSSE; and "message 
content is
      in scope" is scoped to defects in Pekko's handling of that content, not 
the
      application's interpretation of it, which §6 assigns to the application.
    
    ### Result
    
    §14 holds two open questions, Q12 and Q13. Provenance moves to 41 
documented /
    43 maintainer / 4 inferred.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: answer Q12 - no node reads another node's clock, except LWW
    
    ### Motivation
    
    Q12 assumed the failure detector and gossip convergence depend on clocks in 
a
    way an adversary on a cluster member could exploit. Reading the code, they
    largely do not, and the one place wall-clock time does cross nodes is 
already
    documented on the type.
    
    ### Modification
    
    **§14 Q12 - answered**, with the findings:
    
    - The failure detector reads only the local clock
      (`FailureDetector.scala:60`, used at `PhiAccrualFailureDetector.scala:144`
      and `:188`). No remote timestamp reaches it.
    - `Heartbeat.creationTimeNanos` is the sender's own `System.nanoTime()`
      (`ClusterHeartbeat.scala:130`), echoed back verbatim by the receiver
      (`:58`) and subtracted against the sender's own clock (`:251`). No node
      interprets another node's clock value, and the result feeds only a debug
      log guarded by `verboseHeartbeat`.
    - Gossip ordering uses logical `VectorClock` counters
      (`VectorClock.scala:90-92`), with no wall-clock component.
    - `LWWRegister` and `LWWMap` do merge by wall-clock timestamp across nodes.
      The requirement for synchronised clocks, and the alternatives, are stated 
in
      the `LWWRegister` scaladoc.
    
    §5's clock assumption is rewritten to match: it previously implied a
    dependency that the failure detector and gossip do not have.
    
    ### Result
    
    §14 holds one open question, Q13. Provenance moves to 43 documented / 44
    maintainer / 3 inferred.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: answer Q13 - classic remoting is still supported
    
    ### Motivation
    
    Q13 was the last open question. Classic remoting is deprecated but still
    shipped and still CI-gated, and deprecation is not desupport.
    
    ### Modification
    
    - **§14 Q13 - answered.** Security reports against classic remoting are
      accepted on the same terms as artery: configuration is the operator's per
      §5b, and implementation bugs will be looked at per §5b.4. Its §5a settings
      follow the same rulings as their artery equivalents.
    - **§2** - the remoting row names classic remoting alongside artery, rather
      than describing the family as artery only.
    - **§14** - retitled, since every question now carries a ruling. The section
      keeps the questions in place so the reasoning behind each answer stays
      visible and cross-references continue to resolve.
    
    ### Result
    
    All thirteen questions are answered. Provenance moves to 43 documented / 48
    maintainer / 3 inferred, the remaining three being the residual module 
in/out
    split noted in §2 and Q6.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: make SECURITY.md canonical for the reporting policy
    
    ### Motivation
    
    Q10 recorded three documents each canonical for something, and noted that 
the
    reporting wording was duplicated between `SECURITY.md` and the documentation
    site's security index, which had already drifted apart once.
    
    ### Modification
    
    `SECURITY.md` is now the strongest and canonical statement of the reporting
    policy. Other documents link to it rather than restating it, and link to the
    threat model for scope. Where another document carries a reporting statement
    that `SECURITY.md` does not, it is referred to the maintainers to decide
    whether to promote or drop it, rather than being migrated silently.
    
    ### Result
    
    The reporting policy has one owner. The documentation-site page keeps
    announcements and its index of security material.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: cite the paradox docs for the LWW clock requirement
    
    ### Motivation
    
    Q12 and §5 cited only the `LWWRegister` scaladoc for the synchronised-clock
    requirement. The published documentation states it more fully, in the 
section
    on `LWWMap` and again under `LWWRegister`, along with the custom `Clock` and
    `reverseClock` alternatives.
    
    ### Modification
    
    Both citations now name `typed/distributed-data.md` alongside the scaladoc.
    
    ### Result
    
    The citation points where a reader would look first.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: complete the §10 operator contract
    
    ### Motivation
    
    §5b makes §10 the operator's half of the contract: Pekko states what it does
    not provide, and states what it expects instead. That only holds if §10 is
    complete. Auditing it against `reference.conf` and the remoting 
documentation
    found four obligations the model relies on but never stated, and two 
existing
    items that were narrower than the code.
    
    ### Modification
    
    Corrected:
    
    - **10.4** - the `SECURITY` marker is not only the Java serializer's. The 
TLS
      providers use it to report a disabled hostname verification and surviving
      default keystore passwords, so the item now covers the marker rather than
      one emitter.
    - **10.8** - environment substitution was only half the password obligation.
      Both SSL blocks ship `changeme` as the keystore, key and truststore 
password
      (`reference.conf:701`, `:1205`), and Pekko warns under the `SECURITY` 
marker
      if those survive into a running system.
    
    Added:
    
    - **10.10** - protect the key material itself. §6 trusts keystores, 
truststores
      and PEM material as operator-supplied; §10.5 scoped what a certificate 
grants
      but nothing stated the file-level counterpart.
    - **10.11** - keep message-content logging off. `log-received-messages`,
      `log-sent-messages`, `pekko.actor.debug.receive` and `log-config-on-start`
      all default to `off`; turning any on moves payloads, or the secrets that
      §10.8 places in the environment, into the logging system.
    - **10.12** - transport encryption exists only on `tls-tcp`. On `tcp` and
      `aeron-udp` network isolation is not one control among several, it is the
      only one.
    - **10.13** - trust, or secure, the discovery mechanism. §6 marks resolver
      responses as potentially attacker-influenced; §10 asked nothing of the
      operator about it.
    
    ### Result
    
    Every input §6 marks as trusted, and every §9 property Pekko disclaims, now 
has
    a matching operator responsibility in §10.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: narrow Q8 - CPU cost is not in itself a security concern
    
    ### Motivation
    
    Q8 was recorded as "super-linear in message size is a bug" and routed to
    `VALID`, which made a whole class of CPU reports security findings. That was
    wider than intended, in two ways.
    
    First, it conflated "a bug" with "a vulnerability". §5b.4 already provides 
for
    defects Pekko fixes without their being security issues, and a performance
    defect belongs there.
    
    Second, the trigger did not match the intent. "Super-linear in message size"
    fires on an ordinary message that is merely large, whereas the concern is an
    input crafted so that cost explodes disproportionately to it.
    
    ### Modification
    
    - **§14 Q8** - CPU cost is not in itself a security concern; a report of 
extra
      work or degraded throughput is a performance issue. The exception is
      disproportionate cost, where work grows super-linearly in the size or
      structure of a single input. The test is the cost, not the appearance of 
the
      input: an input that looks out of the ordinary is the usual sign of such a
      path rather than a separate condition. Constant-factor overhead is never a
      finding, and volume from an associated peer stays out of model per §7.
    - **§6** - the size and rate note said the question was open. Frame-size 
limits
      and failure-detector bounds are recorded as tuning parameters, not 
security
      controls.
    - **§9** - the resource-exhaustion entry carries the ruling rather than
      deferring to it.
    
    ### Result
    
    Performance reports are triaged as performance, and only disproportionate 
cost
    reaches a security disposition.
    
    ### Tests
    
    Not run - docs only
    
    ### References
    
    Refs #3478
    
    * docs: promote the threat model out of draft status
    
    ### Motivation
    The header still read "DRAFT - awaiting Pekko PMC review. Not yet
    maintainer-ratified." That stopped being true once the PMC chair answered 
all
    thirteen open questions: 48 of the document's claims now carry (maintainer)
    provenance. A triager citing §9 to close a report should not be citing a
    document that describes itself as unratified.
    
    ### Modification
    - Status: replaced the draft banner with "Reviewed by the Apache Pekko PMC,
      2026-08-27", pointing at §14 for the rulings and naming the one item that 
is
      still open (the residual module in/out split, §2 and Q6).
    - Renamed "Draft confidence" to "Provenance mix"; the counts and the
      explanation beneath it were already current and are unchanged.
    
    No claim, ruling, citation or disposition is altered.
    
    ### Result
    The document no longer disclaims its own authority. The single remaining
    inferred item stays visible in the header rather than being smoothed over.
    
    ### Tests
    Not run - docs only
    
    ### References
    Refs #3478
    
    * docs: correct the shutdown-hook count - there are two, not one
    
    ### Motivation
    Review catch from @samueleresca on #3478. §5 and the Q7 answer both stated 
that
    Pekko registers one JVM shutdown hook, via CoordinatedShutdown. That is 
wrong
    for any node running Artery.
    
    ### Modification
    grep over the main sources returns two addShutdownHook sites:
    
      actor/.../actor/CoordinatedShutdown.scala:381   - the one already 
documented
      remote/.../artery/ArteryTransport.scala:392     - registered on transport 
start
    
    The second is gated on system.settings.JvmShutdownHooks, and
    actor/src/main/resources/reference.conf:98 ships jvm-shutdown-hooks = on, so
    both hooks are present under default configuration whenever remoting starts.
    §5 and Q7 now say two, name both sites, and record the config gate - the
    conditionality is the part an integrator needs, not the count on its own.
    
    The two other hits (cluster NodeChurnSpec, StressSpec) are multi-jvm test
    sources and out of model per §3.
    
    Also drops one sentence of editorialising from the provenance line, per 
review:
    it described how much of the v0 draft rested on inference, which stopped 
being
    true once the maintainer rulings landed. The counts are unchanged.
    
    ### Result
    The "what Pekko does not do to its host" inventory is accurate. This was the
    section flagged in the original draft as the highest-priority confirmation
    target precisely because negative claims are hard to cite - and it took a
    maintainer reading the code to find the gap.
    
    ### Tests
    Not run - docs only
    
    ### References
    Refs #3478
    
    * docs: tighten threat model on association, node loss, DNS parsing and 
remote deployment
    
    Motivation:
    Reviewing the draft against the code surfaced four places where the wording
    let real findings fall between dispositions.
    
    Modification:
    - §7/Q1: define "associated" as authenticated. On tcp the handshake is
      unauthenticated, so completing it must not grant trusted-peer standing.
    - Q8: fatal errors in the shared Artery inbound stream escalate to
      ActorSystem termination, so disproportionate cost includes node loss.
    - §9: discovery trusts resolver answers, but parsing of a DNS packet is
      Pekko's own and is done before the transaction-id check.
    - P3/§11a: remote deployment is remote instantiation of classpath actor
      classes with peer-supplied constructor arguments, bounded by P4.
    
    Result:
    Each of the four cases now routes to exactly one §13 disposition.
    
    Tests:
    Not run - docs only
    
    References:
    Refs #3478, Refs #3490
    
    ---------
    
    Co-authored-by: PJ Fanning <[email protected]>
---
 AGENTS.md                               |   6 +
 SECURITY.md                             |  50 ++++
 THREAT_MODEL.md                         | 424 ++++++++++++++++++++++++++++++++
 docs/src/main/paradox/security/index.md |   6 +-
 4 files changed, 485 insertions(+), 1 deletion(-)

diff --git a/AGENTS.md b/AGENTS.md
index 9e42d6b236..0f92d2b5c9 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -225,3 +225,9 @@ Fixes #1234, Refs #1234, or None - <short context>
 - Never omit `References`.
 - Use `Refs #...`, `Fixes #...`, or `None - <short context>`.
 - Do not add `Co-authored-by` or AI-assistant trailers to commits or PR 
descriptions.
+
+## Security
+
+- Read [`SECURITY.md`](SECURITY.md) before reporting or acting on a security 
issue.
+- Classify security findings against the threat model it links,
+  [`THREAT_MODEL.md`](THREAT_MODEL.md); §11a lists recurring false positives.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000000..e94b88f574
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,50 @@
+# Security Policy
+
+## Reporting a Vulnerability
+
+**Do not report security vulnerabilities through public GitHub issues, pull
+requests, or the mailing lists.**
+
+Report them privately to the Apache Security team:
+
+    [email protected]
+
+Apache Pekko does not operate a separate project security list; reports go to
+the foundation-wide address above, which routes to the Pekko PMC.
+
+Please follow the [guidelines laid down by the Apache Security
+team](https://www.apache.org/security/). The Pekko PMC will coordinate
+responsible disclosure with affected upstream maintainers where needed.
+
+To receive security announcements, subscribe to the [Apache Announce Mailing
+List](https://lists.apache.org/[email protected]).
+
+## Security Model
+
+Before reporting, please read Apache Pekko's threat model:
+
+[THREAT_MODEL.md](THREAT_MODEL.md)
+
+It states what Pekko treats as a vulnerability and what it does not — in
+particular its assumptions about the network remoting runs on, which
+configuration defaults change the security envelope, and which properties
+Pekko explicitly leaves to the operator. Reports that fall outside the model
+will be closed citing the relevant section, so checking first will save you
+time.
+
+Two points catch most reporters:
+
+- **Pekko remoting assumes a trusted network.** An `ActorSystem` is not
+  designed to be exposed to an untrusted network; it is expected to be
+  protected by network security such as a firewall, and optionally TLS with
+  mutual authentication. See §4 and §7.
+- **All nodes in a Pekko cluster are trusted equally.** There is no
+  Byzantine-peer model — a finding whose precondition is "a cluster member
+  misbehaves" is out of model. See §7.
+
+## Further Security Documentation
+
+- [Apache Pekko security 
announcements](https://pekko.apache.org/docs/pekko/current/security/)
+- [Remote 
Security](https://pekko.apache.org/docs/pekko/current/remoting-artery.html#remote-security)
+- [Java 
Serialization](https://pekko.apache.org/docs/pekko/current/serialization.html#java-serialization)
+- [Remote deployment allow 
list](https://pekko.apache.org/docs/pekko/current/remoting.html#remote-deployment-allow-list)
diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md
new file mode 100644
index 0000000000..c880f8f36a
--- /dev/null
+++ b/THREAT_MODEL.md
@@ -0,0 +1,424 @@
+# Apache Pekko — Threat Model
+
+**Status:** Reviewed by the Apache Pekko PMC, 2026-08-27. The thirteen 
questions this document was drafted around are answered in §14 and carry 
*(maintainer)* provenance; the residual module in/out split noted in §2 and Q6 
is the one open item.
+
+| | |
+| --- | --- |
+| **Project** | Apache Pekko (core toolkit) |
+| **Written against** | commit `90b02d60`, `main` |
+| **Date** | 2026-08-27 |
+| **Authors** | ASF Security team, at the request of the Pekko PMC |
+| **Version binding** | This model is versioned alongside the project. A 
report against Pekko version *N* is triaged against the model as it stood at 
*N*, not at `main`. |
+| **Reporting** | Findings that violate a §8 property should be reported per 
[`SECURITY.md`](SECURITY.md). Findings that fall under §3 or §9 will be closed 
citing this document. |
+
+**Provenance legend.** Every non-trivial claim is tagged:
+*(documented)* — stated in Pekko's own docs, cited.
+*(maintainer)* — stated by a Pekko maintainer in review of this document.
+*(inferred)* — reasoned from code or config defaults, **not yet confirmed**; 
each has a matching question in §14.
+
+**Provenance mix:** 43 documented / 48 maintainer / 3 inferred. The §5a 
default rulings — previously the largest inferred block — are now answered by 
the §5b posture statement and §14 Q1 to Q5. What remains inferred is the 
residual module in/out split (§14 Q6).
+
+## §1 Overview
+
+Apache Pekko is a Scala/Java toolkit for building concurrent, distributed and 
resilient message-driven applications. Its unit of computation is the actor: an 
object with private state that communicates only by asynchronous message 
passing. Pekko extends that model across machines — an `ActorSystem` can 
address actors on remote nodes as if they were local, and a cluster of such 
systems provides membership, sharding, singletons and replicated data. That 
transparency is the source of most o [...]
+
+---
+
+## §2 Scope and intended use
+
+Pekko is an **in-process library** that the application embeds and configures. 
It is not a server, not a daemon, and ships no runnable artifact that an 
operator deploys on its own. There is consequently no "Pekko instance" to 
secure independently of the application hosting it.
+
+Three caller roles matter, and they are not equally trusted:
+
+- **The embedding application** — fully trusted. It constructs the 
`ActorSystem`, supplies configuration, and already shares the JVM with Pekko.
+- **The operator/deployer** — trusted for the instance. Chooses transport, TLS 
material, and the network the node sits on. Most of §10 lands here.
+- **The remote peer** — another `ActorSystem` that has associated over 
remoting. Its trust level is the central question of this model, and §7 answers 
it.
+
+### Component families
+
+| Family | Modules | Entry point | Leaves the process? | In model |
+| --- | --- | --- | --- | --- |
+| Actor core | `actor`, `actor-typed`, `slf4j`, `coordination` | 
`ActorSystem`, `ActorRef` | no | **yes** |
+| Classic IO | `actor` (`org.apache.pekko.io`) | `IO(Tcp)`, `IO(Udp)`, 
`IO(Dns)` | **network — if the application binds** | **yes** |
+| Remoting | `remote` | Artery transport (`tcp` / `tls-tcp` / `aeron-udp`); 
classic remoting (netty), deprecated but supported *(maintainer — §14 Q13)* | 
**network** | **yes — primary surface** |
+| Cluster | `cluster`, `cluster-typed`, `cluster-tools`, `cluster-sharding*`, 
`cluster-metrics` | gossip, membership, sharding | **network** (via remoting) | 
**yes** |
+| Replicated data | `distributed-data` | CRDT replication | **network** (via 
remoting) | **yes** |
+| Serialization | `serialization-jackson`, `serialization-jackson3` | 
`Serializer` SPI | deserializes network bytes | **yes — critical** |
+| Wire encoding | `protobuf-v3` | shaded protobuf runtime | parses remoting 
and cluster wire bytes | **yes** |
+| Streams | `stream`, `stream-typed` | `Source`/`Flow`/`Sink`; `Tcp`, `TLS`, 
`FileIO`, `Framing` | **network / filesystem — if the application uses those 
connectors** | **yes** |
+| Persistence | `persistence`, `persistence-typed`, `persistence-query`, 
`persistence-shared` | journal / snapshot plugin SPI | **storage backend** | 
**yes**, boundary at the SPI |
+| PKI | `pki` | PEM/keystore parsing | **reads files** | **yes** |
+| Discovery | `discovery` | service-discovery SPI | **network / DNS** | 
**yes** |
+| OSGi | `osgi` | bundle activator | classloading | **yes** *(maintainer — §14 
Q6)* |
+| Test kits | `*-testkit`, `multi-node-testkit`, `*-tests`, `persistence-tck`, 
`stream-tests-tck` | — | — | **no** — §3 |
+| Benchmarks | `bench-jmh` | — | — | **no** — §3 |
+| Build / docs | `docs`, `project`, `scripts`, `legal`, `kubernetes` 
(test-cluster provisioning scripts), `plugins`, `bill-of-materials`, 
`scala-nightly` | — | — | **no** — §3 |
+
+*(inferred — the in/out split is the ASF Security team's proposal, except 
`osgi`, which is confirmed in model; see §14 Q6)*
+
+---
+
+## §3 Out of scope (explicit non-goals)
+
+- **Test kits, benchmarks, build tooling and documentation sources.** These 
ship in the repository but are not part of the security-supported surface. A 
finding in `bench-jmh` or any `*-tests` module is `OUT-OF-MODEL: 
unsupported-component`. *(inferred — §14 Q6)*
+- **Pekko is not a sandbox.** Actors are not an isolation boundary. Any code 
running in the JVM can reach any actor's state by ordinary means; the actor 
model is a concurrency discipline, not a security control. *(maintainer — §14 
Q4)*
+- **Pekko is not an authorization framework.** It carries no notion of a 
principal, role, or permission on a message. Application-level authorization is 
the embedding application's job. *(maintainer — §14 Q11)*
+- **A Pekko cluster is not a multi-tenancy boundary.** The documentation is 
explicit that *"you'll have to trust all cluster nodes the same in a Pekko 
cluster anyway"* *(documented — `remote-security.md`)*. Separating 
mutually-distrusting tenants across nodes of one cluster is not a supported 
deployment.
+- **Attackers who already control the embedding process** are out of scope — 
they have already won. *(maintainer — §14 Q11)*
+- **This document describes the `apache/pekko` core toolkit.** Its technical 
content — the §2 component families, the §5a defaults, the §8 properties — is 
about that codebase. Other Apache Pekko deliverables (Pekko Management, Pekko 
HTTP, Pekko gRPC, Pekko Connectors, Pekko Projection, the persistence plugins) 
ship from their own repositories, and this model does not enumerate their 
surfaces.
+
+    That is a limit on **coverage, not on standing**. Reporting and triage are 
identical across all Pekko repositories: the same address (see 
[`SECURITY.md`](SECURITY.md)), the same PMC, and the same posture — an 
implementation defect is Pekko's to fix, while a request to change a default is 
a change request under §5b, not a vulnerability. A finding in another Pekko 
repository is **not** `OUT-OF-MODEL: unsupported-component`; that disposition 
is for the §3 components above, not for code  [...]
+
+---
+
+## §4 Trust boundaries and data flow
+
+**The primary trust boundary is the remoting network boundary**, and Pekko's 
documentation places it unusually explicitly:
+
+> "An `ActorSystem` should not be exposed via Pekko Cluster or Pekko Remote 
over plain Aeron/UDP or TCP to an untrusted network, such as the Internet. It 
should be protected by network security, such as a firewall. If that is not 
considered enough protection, TLS with mutual authentication should be enabled."
+> — *(documented — `remote-security.md`)*
+
+and states the consequence of crossing it:
+
+> "As soon as an actor system can connect to another remotely, it may in 
principle send any possible message to any actor contained within that remote 
system."
+> — *(documented — `remote-security.md`, Untrusted Mode)*
+
+Read together, these define the model: **remoting assumes it runs on a network 
the operator has already restricted.** Association is the security decision; 
once a peer is associated, it is inside. Pekko offers two mechanisms to tighten 
that (TLS mutual authentication, untrusted mode) but neither converts remoting 
into a boundary that is safe to face the open internet *(documented — see §9)*.
+
+The application's own boundaries — the HTTP endpoint, the message broker, the 
database — sit **outside** Pekko and are the embedding application's 
responsibility.
+
+### Reachability preconditions per family
+
+A finding must meet its family's precondition to be in-model:
+
+- **Remoting** — reachable from bytes arriving on the Artery transport, *and* 
the report must state whether it assumes the network-isolation assumption above 
is intact. A finding that requires an attacker already on the cluster network 
is judged under §7, not automatically valid.
+- **Serialization** — reachable from a message payload deserialized by a 
**configured, enabled** serializer. Findings reachable only when 
`allow-java-serialization = on` are judged under §5a.
+- **Cluster / distributed-data** — reachable from gossip or replication 
traffic originating at an **associated peer**. Per §7 such a peer is trusted, 
so these are typically out of model unless the finding shows a pre-association 
reach.
+- **Actor core / streams** — reachable from data the embedding application 
passes in. Trusted by default; a finding must show the data crosses an 
application boundary that Pekko itself defines.
+- **Persistence** — reachable from journal or snapshot contents. The store is 
trusted *(maintainer — §14 Q5)*, so a finding requiring write access to it is 
out of model; a defect in Pekko's replay handling is not.
+- **PKI** — reachable from PEM/keystore material. Operator-supplied and 
trusted per §6.
+
+---
+
+## §5 Assumptions about the environment
+
+- **Runtime.** A conformant JVM. Pekko does not defend against a hostile JVM, 
a hostile classpath, or an attacker with local code execution in the same 
process. *(maintainer — §14 Q11)*
+- **Network adjacency.** *"Best practice is that Pekko remoting nodes should 
only be accessible from the adjacent network."* *(documented — 
`remote-security.md`)*
+- **PKI scope.** Where TLS is used, every certificate issued by the same 
internal PKI tree is equivalent: *"there is still a risk that an attacker can 
gain access to a valid certificate by compromising any node with certificates 
issued by the same internal PKI tree."* *(documented — `remote-security.md`)*
+- **Clock.** Failure detection and gossip do **not** depend on other nodes' 
clocks: the failure detector reads only the local clock, and gossip ordering 
uses logical `VectorClock` counters. The exception is `LWWRegister`/`LWWMap`, 
which merge by wall-clock timestamp and therefore assume reasonably 
synchronised clocks across writers *(documented — `typed/distributed-data.md`, 
and the `LWWRegister` scaladoc)*. Pekko does not defend against adversarial 
clock manipulation on a cluster node * [...]
+- **Entropy.** On Linux with SHA1PRNG, the docs recommend 
`-Djava.security.egd=file:/dev/urandom` to avoid blocking, noting it *"is NOT 
as secure because it reuses the seed."* *(documented — `remote-security.md`)*
+
+### What Pekko does not do to its host
+
+These are negative claims, rarely written down anywhere. Each is confirmed and 
carries the citation, or the exception, behind it *(maintainer — §14 Q7)*:
+
+- Installs no signal handlers and spawns no child processes — no 
`sun.misc.Signal`/`SignalHandler`, `Runtime.exec` or `ProcessBuilder` in the 
main sources. It does register **two JVM shutdown hooks**: one via 
`CoordinatedShutdown` (`actor/.../actor/CoordinatedShutdown.scala:381`), and 
one in remoting (`remote/.../artery/ArteryTransport.scala:392`) registered on 
transport start. The second is conditional on `pekko.jvm-shutdown-hooks`, which 
`actor/src/main/resources/reference.conf:98` shi [...]
+- Opens no listening socket of its own accord. Remoting binds when configured; 
`org.apache.pekko.io.Tcp`/`Udp` (in `pekko-actor`) and `stream.scaladsl.Tcp` 
(in `pekko-stream`) bind only on an explicit application call. Pekko never 
binds a port the application did not ask for.
+- **Reads environment variables during configuration startup, but never 
modifies them** *(maintainer — §14 Q7)*. Pekko itself calls neither 
`System.getenv` nor `sys.env` anywhere in the main sources; environment values 
reach it only through HOCON `${?VAR}` substitution when `ConfigFactory.load` 
resolves the configuration (`actor/.../actor/ActorSystem.scala:281`). That path 
is deliberate and documented — it is how the docs tell operators to supply 
passwords (§10.8).
+- Writes to logging via the configured logger. One exception: 
`StandardOutLogger` prints to stdout with `println` 
(`actor/.../event/Logging.scala:1024` onward). It carries the very early 
startup log, before the configured loggers are running, and is bounded by 
`pekko.stdout-loglevel`, which defaults to `WARNING`.
+- Does not mutate process-global state at initialization — no 
`System.setProperty`, `Locale.setDefault` or `TimeZone.setDefault` in the main 
sources.
+
+---
+
+## §5a Configuration variants that change the security envelope
+
+Pekko's security posture is set almost entirely by configuration. **Every row 
below whose default is the weaker value needs a maintainer ruling** — see §14.
+
+| Setting | Default | Effect | Maintainer stance |
+| --- | --- | --- | --- |
+| `pekko.actor.allow-java-serialization` | `off` | On, exposes the JVM 
deserialization attack surface to any message payload. Docs: *"highly 
discouraged to enable in production"* *(documented — `serialization.md`)* | 
Secure default. Enabling it transfers gadget-chain defence to the operator — 
§5b, §14 Q2 *(maintainer)* |
+| `pekko.remote.artery.transport` | `tcp` | **Plaintext.** No peer 
authentication and no confidentiality on the wire. `tls-tcp` opts into TLS | 
Compatibility default — §5b, §14 Q1 *(maintainer)* |
+| `pekko.remote.artery.ssl.config-ssl-engine.require-mutual-authentication` | 
`on` | Both ends present certificates *(documented)* | Secure default |
+| `…ssl.config-ssl-engine.hostname-verification` | `off` | Off, a valid cert 
from the trusted PKI authenticates regardless of which host presents it. Docs 
*recommend* `on` but ship `off` *(documented)* | Compatibility default, warned 
at runtime — §5b, §14 Q3 *(maintainer)* |
+| `pekko.remote.artery.untrusted-mode` | `off` | On, blocks inbound system 
messages, `PossiblyHarmful` messages, remote deployment, remote DeathWatch, and 
actor selections outside `trusted-selection-paths` *(documented)* | Hardening; 
adoption is the operator's call — §5b, §14 Q4 *(maintainer)* |
+| `pekko.remote.artery.trusted-selection-paths` | `[]` | Allow-list of actor 
paths that may receive selections under untrusted mode *(documented)* | Follows 
Q4 |
+| `pekko.remote.deployment.enable-allow-list` | `off` | On, restricts which 
actor classes a peer may remote-deploy *(documented — `remoting.md`)* | 
Hardening; adoption is the operator's call — §5b, §14 Q4 *(maintainer)* |
+| `pekko.remote.classic.untrusted-mode` | `off` | Classic-remoting equivalent 
of the artery flag *(reference.conf:381)* | Follows Q4 *(maintainer — §14 Q13)* 
|
+| `pekko.remote.classic.trusted-selection-paths` | `[]` | As above 
*(reference.conf:387)* | Follows Q4 |
+| `pekko.remote.classic.netty.tcp.enable-ssl` | `false` | Classic's default 
transport is plaintext netty TCP *(reference.conf:582)* | Follows Q1 
*(maintainer — §14 Q13)* |
+| `pekko.actor.serialize-messages` / `serialize-creators` | `off` | Testing 
aids that force serialization round-trips. Not security controls. Docs: *"this 
is only intended for testing"* *(documented — 
`actor/src/main/resources/reference.conf`)* | Not a security knob |
+
+---
+
+## §5b Security posture: hardening, not secure-by-default
+
+Pekko is a long-lived toolkit whose deployment base is inherited from Akka. 
Its configuration defaults are chosen for compatibility with those deployments, 
in which operators have already been tasked with securing the network Pekko 
runs on and controlling who may reach and message a deployed system. Changing a 
default to a more restrictive value breaks those deployments on upgrade, 
sometimes without a clear signal as to why.
+
+Pekko therefore takes the following position *(maintainer)*:
+
+1. **Defaults are compatibility choices, not security claims.** §5a lists 
every setting whose default affects the security envelope; §10 lists what the 
operator must do as a result. Read together they are the contract: Pekko states 
what it does not provide, and states what it expects of the operator instead.
+2. **A report that a default should be more restrictive is not a vulnerability 
report.** It is a change request, and is closed as `BY-DESIGN: 
default-configuration` (§13).
+3. **Proposals to change a default are welcome, and belong on the development 
list.** The PMC will weigh them in good faith on their merits — the 
compatibility cost, whether a migration path exists, and whether a major 
version is in flight. Defaults can and do change; they change through project 
discussion, not as the remediation of a security report.
+4. **If an implementation is wrong, Pekko fixes it.** Where a control does not 
do what it is documented to do once enabled, that is a defect, in scope, at the 
severity §8 assigns. This posture governs which value ships as the default — 
never whether the mechanism works.
+
+---
+
+## §6 Assumptions about inputs
+
+For a toolkit whose surface is a wire protocol, the useful table is keyed by 
**message class**, not by function.
+
+| Source | Input | Attacker-controllable? | Who must enforce what |
+| --- | --- | --- | --- |
+| Artery transport | Inbound frame headers / framing | **Yes if the 
network-isolation assumption fails** | Operator: network isolation (§10), or 
TLS |
+| Artery transport | Serialized user-message payload | **Yes**, from an 
associated peer | Pekko: only enabled serializers run. App: validate semantic 
content |
+| Artery transport | System messages (`Create`, `Terminate`, `Watch`, 
`Supervise`) | **Yes**, from an associated peer unless untrusted mode is on | 
Operator: `untrusted-mode` if peers are less than fully trusted |
+| Artery transport | `PossiblyHarmful` messages (`PoisonPill`, `Kill`) | 
**Yes**, same condition | Same |
+| Artery transport | Remote-deployment `Props` | **Yes**, same condition | 
Operator: `enable-allow-list` |
+| Cluster | Gossip / membership state | From an **associated** peer — trusted 
per §7 | — |
+| `distributed-data` | Replicated CRDT deltas | From an associated peer — 
trusted per §7 | — |
+| Persistence | Journal / snapshot contents on replay | **No** — the store is 
trusted *(maintainer — §14 Q5)* | Operator/DBA: secure the store |
+| `pki` | PEM / keystore files | **No** — operator-supplied, trusted | 
Operator: protect key material |
+| `discovery` | Service-discovery responses (DNS, K8s API) | **Potentially** — 
depends on the resolver | Operator: trust the discovery mechanism |
+| `io.Tcp` / `io.Udp` | Bytes on an application-bound socket | **Potentially** 
— depends where the application exposes it | App: it chose to bind, defines the 
protocol, and owns what it exposes |
+| `stream.Tcp` / `stream.TLS` | Bytes on an application-bound stream server | 
**Potentially** — as above | App: as above. TLS itself is delegated to the 
JDK's JSSE |
+| `Framing` / `JsonFraming` | Delimited or length-prefixed frames | 
**Potentially** where fed from a network source | App: supplies 
`maximumFrameLength`, which bounds frame size |
+| `stream.FileIO` | File contents at an application-supplied path | Depends on 
the path | App |
+| Config | `application.conf`, system properties | **No** — trusted, part of 
the deployment | Operator |
+
+**Size and rate.** Artery imposes frame-size limits and the failure detector 
bounds how long an unresponsive peer is tolerated. These are tuning parameters, 
not security controls *(maintainer — §14 Q8)*.
+
+---
+
+## §7 Adversary model
+
+**In scope:**
+
+- **An unassociated network attacker** who can reach the remoting port — in a 
deployment where the operator's isolation assumption has held, this attacker 
should not exist; where it does, the relevant question is whether they can 
achieve anything **before** association completes. Pre-association reachability 
is the sharpest in-model attack surface. *(maintainer — §14 Q1)*
+
+  **"Associated" in this section means *authenticated*.** On the default `tcp` 
transport, association is a single unauthenticated `HandshakeReq` that the 
receiver accepts if it names the receiver's own address 
(`remote/.../artery/Handshake.scala`) — it establishes no identity and admits 
nothing about the sender. A host that completes that handshake is still the 
unassociated network attacker above; it does not acquire the trusted-peer 
standing that the exclusions below grant. Trusted-peer [...]
+- **An attacker supplying message content** to an otherwise legitimate peer — 
e.g. data that originates at the application's own untrusted edge and is 
forwarded into an actor message. *(maintainer — §14 Q11)*
+
+**Explicitly out of scope:**
+
+- **An authenticated peer behaving arbitrarily.** There is no Byzantine-peer 
model. The documentation is direct: *"you'll have to trust all cluster nodes 
the same in a Pekko cluster anyway"* *(documented)*, and *"as soon as an actor 
system can connect to another remotely, it may in principle send any possible 
message to any actor contained within that remote system"* *(documented)*. **A 
finding whose precondition is "a cluster member misbehaves" is out of model** — 
there is no honest-maj [...]
+- **An attacker holding any certificate from the cluster's PKI tree.** 
Documented as equivalent to cluster access *(documented)*.
+- **An attacker with code execution in the embedding JVM.** Already inside the 
trust boundary.
+- **Side-channel observers.** Pekko makes no timing or memory-access 
guarantees. *(maintainer — §14 Q11)*
+
+---
+
+## §8 Security properties Pekko provides
+
+| # | Property & conditions | Violation symptom | Severity | Provenance |
+| --- | --- | --- | --- | --- |
+| P1 | **Java serialization is disabled by default**; Pekko uses it for none 
of its own internal messages | A payload deserialized via Java serialization 
under default config | **Critical** — RCE class | *(documented — 
`serialization.md`)* |
+| P2 | The disabled Java serializer **logs rejected attempts** under the 
`SECURITY` marker, and those *"SHOULD be treated as potential attacks which the 
serializer prevented"* | Silent acceptance where rejection + log is expected | 
High | *(documented — `serialization.md`)* |
+| P3 | **Remote deployment is not remote code loading.** The actor class must 
already be present on the target system. It **is** remote instantiation of any 
actor class on the target's classpath with peer-supplied constructor arguments 
(`remote/.../serialization/DaemonMsgCreateSerializer.scala`), bounded only by 
P4 when `enable-allow-list` is on *(maintainer)* | A peer causing a class 
absent from the target's classpath to execute | **Critical** | *(documented — 
`remoting.md`)* |
+| P4 | With `enable-allow-list = on`, only listed actor classes may be 
remote-deployed onto this node | An unlisted class deployed | High | 
*(documented — `remoting.md`)* |
+| P5 | With `untrusted-mode = on`, inbound system messages, `PossiblyHarmful` 
messages, remote deployment, remote DeathWatch and non-allow-listed actor 
selections are **dropped and logged** | Any of these taking effect despite the 
flag | High | *(documented — `remote-security.md`)* |
+| P6 | With `transport = tls-tcp`, TLS is applied and **mutual authentication 
is on by default** — the server side also requests and verifies the client's 
certificate | Association completing without peer certificate verification | 
**Critical** | *(documented — `remote-security.md`)* |
+| P7 | Certificate rotation is supported for mTLS in Kubernetes without 
cluster restart | Rotation causing association failure or silent downgrade | 
Medium | *(documented — `remote-security.md`; **2.0+ only**, absent from the 
released 1.x docs — 
[snapshot](https://pekko.apache.org/docs/pekko/snapshot/remote-security.html#mtls-with-rotated-certificates-in-kubernetes))*
 |
+
+**Note the shape of this list:** P4, P5 and P6 are all *conditional on a 
non-default setting*. Under stock configuration, the properties Pekko actively 
provides at the network boundary are P1, P2 and P3 — the rest of the posture is 
delegated to the operator via §10. This is a deliberate design, but it is the 
single most important thing for a triager to understand.
+
+---
+
+## §9 Security properties Pekko does **not** provide
+
+- **No peer authentication by default.** With the default `transport = tcp` 
there is no shared secret, no certificate, and no handshake credential. Any 
host that can reach the port and speak Artery can attempt association. 
*(maintainer — §14 Q1; §5b)*
+- **No confidentiality or integrity on the wire by default.** Same cause. 
*(maintainer — §14 Q1; §5b)*
+- **No intra-cluster authorization.** Once associated, a peer may address any 
actor in the system. There is no per-actor, per-message, or per-peer permission 
model. *(documented)*
+- **No Byzantine fault tolerance, and no compromised-node detection.** See §7. 
Cluster protocols assume members are honest; there is no threshold below which 
arbitrary member behaviour is tolerated *(documented — `remote-security.md`)*, 
and Pekko offers no mechanism that would identify a member as compromised in 
the first place *(maintainer — §14 Q9)*.
+- **No bound on blast radius from one compromised node.** Documented 
explicitly for the PKI case *(documented)*.
+- **No protection once Java serialization is enabled.** Turning it on 
re-exposes the full JVM deserialization surface; the docs place this squarely 
on the operator. *(documented)*
+
+### False friends
+
+These are the assumptions integrators most often bring with them, and each is 
wrong:
+
+- **Untrusted mode is not a security boundary.** *"Untrusted mode does not 
give full protection against attacks by itself. It makes it slightly harder to 
perform malicious or unintended actions"* *(documented — 
`remote-security.md`)*. It is hardening. Treating it as a substitute for 
network isolation is a §11 misuse.
+- **`PossiblyHarmful` is a marker, not an authorization mechanism.** It is a 
compile-time trait that untrusted mode consults. It confers no protection when 
untrusted mode is off.
+- **A service mesh is not a substitute for remoting security.** *"Encryption 
and authentication via a service mesh is not a replacement for Pekko Cluster 
remoting security"* — Pekko's peer-to-peer addressing has requirements a mesh 
does not satisfy *(documented — `remote-security.md`; **2.0+ only**, absent 
from the released 1.x docs — 
[snapshot](https://pekko.apache.org/docs/pekko/snapshot/general/remoting.html#service-mesh))*.
+- **TLS mutual authentication does not give per-node identity guarantees by 
default**, because `hostname-verification` ships `off` — any cert from the 
trusted PKI authenticates as any node. *(documented + config default)*
+- **The actor boundary is not a security boundary.** Message-passing isolation 
is a concurrency property, not a confidentiality one.
+
+### Well-known attack classes left to the caller
+
+- **JVM deserialization gadget chains** — mitigated by P1 only so long as Java 
serialization stays off, and only for payloads Pekko itself deserializes; 
application-level serializers are the application's problem. Pekko integrates 
**no** serialization filter: `JavaSerializer.fromBinary` performs an unfiltered 
`ObjectInputStream.readObject` (`actor/.../serialization/Serializer.scala`). An 
operator who enables Java serialization must supply the allow list themselves 
through the JVM — `-Djd [...]
+- **Resource-exhaustion via message volume or size** — CPU cost is not in 
itself a security concern, and volume from an associated peer is out of model. 
Only disproportionate cost, where work explodes relative to a single input, is 
a defect. See §14 Q8.
+- **DNS / service-discovery spoofing** — `discovery` trusts the *answers* of 
the resolver it is configured with. That trust covers the content of a 
response, not its parsing: a DNS packet is unauthenticated network input, 
`pekko-actor`'s async resolver parses it (`actor/.../io/dns/internal`) before 
matching the transaction id, and a defect in that parsing is Pekko's own per 
§14 Q11. *(maintainer)*
+- **Storage-layer tampering** on persistence journals and snapshot stores — 
the store is trusted, and securing it belongs to whoever administers it 
*(maintainer — §14 Q5)*.
+
+---
+
+## §10 Downstream responsibilities
+
+The operator or embedding application must:
+
+1. **Keep remoting off untrusted networks.** Firewall the remoting port to the 
adjacent network. This is the assumption the whole model rests on 
*(documented)*.
+2. **Enable `tls-tcp` if the network is not sufficiently trusted**, and set 
`hostname-verification = on` unless hostnames are genuinely dynamic 
*(documented)*.
+3. **Leave `allow-java-serialization = off`.** If it must be enabled for 
legacy compatibility, treat the deployment as having no deserialization 
protection *(documented)*, and **maintain your own gadget-chain allow list** 
via `-Djdk.serialFilter` or a process-wide `ObjectInputFilter`. Pekko supplies 
no filter of its own, and findings that require the flag to be on are out of 
model *(maintainer — §14 Q2)*.
+4. **Treat `SECURITY`-marked log entries as security signals**, not noise. The 
disabled Java serializer marks rejected attempts this way *(documented)*; the 
TLS providers use the same marker to report a disabled hostname verification 
and the use of default keystore passwords *(`ConfigSSLEngineProvider`)*.
+5. **Scope the PKI tree to the cluster.** Any certificate it issues is cluster 
access *(documented)*.
+6. **Consider `untrusted-mode = on` and `enable-allow-list = on`** where peers 
are less than fully trusted — understanding both are hardening, not boundaries.
+7. **Never place mutually-distrusting tenants in one cluster** *(documented)*.
+8. **Supply passwords by environment substitution, not literals in config 
files** *(documented)*, and **replace the shipped defaults**. Both SSL blocks 
ship `key-store-password`, `key-password` and `trust-store-password` as 
`"changeme"` (`reference.conf:701`, `:1205`); Pekko warns under the `SECURITY` 
marker if they survive into a running system.
+9. **Secure the persistence store.** Pekko trusts journal and snapshot 
contents on replay and does not validate them as potentially hostile, so access 
control and integrity for the database or store are the administrator's 
responsibility *(maintainer — §14 Q5)*.
+10. **Protect the key material itself.** §10.5 scopes what a certificate 
grants; this is the file-level counterpart. Keystores, truststores and PEM 
material are operator-supplied and trusted by §6 — Pekko validates neither 
their provenance nor their permissions.
+11. **Keep message-content logging off in production.** 
`log-received-messages` and `log-sent-messages` (`reference.conf:862`, `:866`, 
and `:393`, `:397` for classic) and `pekko.actor.debug.receive` 
(`actor/.../reference.conf:787`) all write message contents to the log, and 
`pekko.log-config-on-start` (`:50`) writes the resolved configuration — which, 
per §10.8, is where secrets pulled in by environment substitution end up. All 
four default to `off`; turning any on moves payloads or cred [...]
+12. **Understand that transport encryption exists only on `tls-tcp`.** Neither 
`tcp` nor `aeron-udp` offers an encryption option *(documented — 
`remote-security.md`)*, so on those transports network isolation is not one 
control among several, it is the only one.
+13. **Trust, or secure, the discovery mechanism.** `discovery` accepts 
whatever its configured resolver returns (§6); Pekko does not authenticate DNS 
or service-registry responses.
+
+---
+
+## §11 Known misuse patterns
+
+- **Exposing remoting or cluster ports to the internet**, on the theory that 
TLS or untrusted mode makes it safe. Both are explicitly documented as 
insufficient alone.
+- **Enabling `allow-java-serialization` for prototyping convenience and 
shipping it.** The docs anticipate exactly this path.
+- **Relying on a service mesh** for cluster authentication *(documented as 
insufficient)*.
+- **Using cluster membership as a tenancy or privilege boundary.**
+- **Enabling TLS but leaving `hostname-verification = off`** and assuming 
per-node identity.
+- **Treating actor encapsulation as a confidentiality guarantee** within a 
single JVM.
+
+---
+
+## §11a Known non-findings (recurring false positives)
+
+Feed this section to scanners and AI triage as a suppression list.
+
+- **"Remote deployment permits arbitrary class instantiation."** Remote 
deployment is *not* remote code loading — the class must already be on the 
target's classpath *(P3, documented)*. Absent a way to introduce a class, this 
is not RCE. It is remote instantiation of classpath actor classes with 
peer-chosen arguments (P3); a report that names a specific classpath class 
whose construction has a harmful side effect is judged under §7 for the peer 
and P4 for the allow list, not as this non- [...]
+- **"Remoting transport defaults to plaintext."** Correct, and by design under 
the §4 network-isolation assumption. In-model only if the finding shows harm 
reachable **pre-association**; a request to change the default is `BY-DESIGN: 
default-configuration` per §5b.
+- **"`PoisonPill` can be sent remotely to shut down a system."** Documented 
behaviour, gated by `untrusted-mode` *(documented — `remote-security.md`)*.
+- **"`hostname-verification` is disabled by default."** Known and documented, 
with the trade-off spelled out for dynamic-hostname deployments, and warned at 
runtime under `LogMarker.Security`. A request to change the default is 
`BY-DESIGN: default-configuration` per §5b.
+- **Findings in `*-tests`, `*-testkit`, `bench-jmh`, `docs`** — `OUT-OF-MODEL: 
unsupported-component` per §3.
+- **"SHA1PRNG with `/dev/urandom` reuses the seed."** Documented trade-off, 
deliberately recommended to avoid blocking *(documented)*.
+
+---
+
+## §12 Conditions that would change this model
+
+- A change to any §5a **default**, particularly `transport`, `untrusted-mode`, 
or `allow-java-serialization`.
+- A new transport, or a new wire protocol at the remoting layer.
+- Any per-peer or per-actor authorization mechanism — that would create an 
intra-cluster trust boundary this model says does not exist.
+- Promotion of a §3 module into the supported surface, or **removal of a 
module from it** — `osgi` is a candidate for removal (§14 Q6), and dropping it 
would move findings there to `OUT-OF-MODEL: unsupported-component`.
+- **A report that cannot be routed to exactly one §13 disposition.** That is 
evidence of a model gap; the correct response is to revise this document, not 
to make an ad-hoc call.
+
+---
+
+## §13 Triage dispositions
+
+| Disposition | Meaning | Licensed by |
+| --- | --- | --- |
+| `VALID` | Violates a §8 property via an in-scope adversary and input | §6, 
§7, §8 |
+| `VALID-HARDENING` | No §8 property violated, but the API makes a §11 misuse 
easy enough to warrant hardening. No CVE by default | §11 |
+| `OUT-OF-MODEL: trusted-input` | Requires control of an input §6 marks 
trusted (config, PEM material, journal and snapshot contents, 
application-supplied data) | §6 |
+| `OUT-OF-MODEL: adversary-not-in-scope` | Requires an associated peer to 
misbehave, a PKI-tree certificate, or in-JVM code execution | §7 |
+| `OUT-OF-MODEL: unsupported-component` | Lands in a §3 module | §3 |
+| `OUT-OF-MODEL: non-default-build` | Only manifests under a non-default §5a 
setting — most often `allow-java-serialization = on` | §5a |
+| `BY-DESIGN: property-disclaimed` | Concerns a §9 property Pekko explicitly 
does not provide | §9 |
+| `BY-DESIGN: default-configuration` | Asks that a §5a default be changed to a 
more restrictive value. Not a vulnerability; §5b.3 invites the proposal on the 
development list | §5b |
+| `KNOWN-NON-FINDING` | Matches a §11a pattern | §11a |
+| `MODEL-GAP` | Routable to none of the above — triggers §12 | §12 |
+
+---
+
+## §14 Maintainer rulings (formerly open questions)
+
+**All thirteen questions are answered.** They are retained in place, with 
their answers, so that cross-references elsewhere in this document continue to 
resolve and the reasoning behind each ruling stays visible. What remains 
inferred is noted in §2 and Q6: the residual module in/out split.
+
+**Q1 — The plaintext transport default. ANSWERED *(maintainer)*.**
+`transport` ships `tcp`, so a stock cluster has no peer authentication. 
**Answer:** the default is a compatibility choice under §5b, not a security 
claim. Reports route as follows:
+- "the default should be `tls-tcp`" → `BY-DESIGN: default-configuration`; 
§5b.3 invites the proposal on the development list.
+- "an unauthenticated peer can associate", assuming internet exposure → 
`BY-DESIGN: property-disclaimed`; §9 disclaims peer authentication by default. 
(The draft proposed `OUT-OF-MODEL: adversary-not-in-scope`, which does not fit: 
§13 defines that disposition as requiring an associated peer, a PKI-tree 
certificate, or in-JVM execution, and §7 lists the unassociated network 
attacker as **in scope**.)
+- harm reachable **pre-association** from an adjacent-network host → `VALID`.
+- harm reachable from an adjacent-network host that has completed the 
**unauthenticated** `tcp` handshake but holds no operator-granted standing (§7: 
*associated* means *authenticated*) → `VALID`. Completing that handshake does 
not convert the attacker into the trusted peer §7 excludes; "pre-association" 
is read as "pre-authentication". *(maintainer)*
+
+**Q2 — `allow-java-serialization`. ANSWERED *(maintainer)*.** Enabling it is 
not recommended. **Answer:** any finding that requires 
`allow-java-serialization = on` to manifest is `OUT-OF-MODEL: 
non-default-build`, including gadget-chain deserialization. An operator who 
enables it takes on gadget-chain defence **entirely**: Pekko integrates no 
serialization filter, so the only lever is the JVM's own — 
`-Djdk.serialFilter`, or an `ObjectInputFilter` installed process-wide. 
Maintaining that [...]
+
+Two things this does **not** dispose of, per §5b.4:
+- A serious defect in Pekko's own serialization implementation is in scope and 
may warrant a CVE, whatever the flag is set to.
+- Java deserialization occurring **despite** `allow-java-serialization = off` 
violates §8 P1 and is `VALID`, Critical.
+
+**Q3 — `hostname-verification = off`. ANSWERED *(maintainer)*.** **Answer:** a 
compatibility default under §5b, deliberate rather than legacy, supporting 
deployments where hostnames are dynamic and not known up front. Pekko 
additionally warns at runtime under `LogMarker.Security` whenever TLS is 
enabled and verification is off, on both transports 
(`artery/tcp/ConfigSSLEngineProvider.scala`, 
`transport/netty/SSLEngineProvider.scala`), so the operator is told at startup. 
A report that "any [...]
+
+**Q4 — Are `untrusted-mode` and `enable-allow-list` security boundaries or 
hardening? ANSWERED *(maintainer)*.** **Answer:** both are **hardening** 
features, per the documented *"does not give full protection"*, and shipping 
them `off` is a §5b compatibility choice — adoption is the operator's decision. 
A request that either default to `on` is `BY-DESIGN: default-configuration`.
+
+A **bypass of either once enabled** is a separate matter and is **not** 
covered by that. It violates §8 P5 or P4 respectively, so it is `VALID` at the 
severity §8 assigns, per §5b.4. The draft proposed `VALID-HARDENING` for this 
case, which §13 defines as *"No §8 property violated"* — that cannot apply to a 
bypass of a control §8 credits.
+
+**Q5 — Persistence backend trust. ANSWERED *(maintainer)*.** **Answer:** 
journal and snapshot stores are **trusted**. Securing the database or 
persistence store is the responsibility of whoever administers it, and Pekko 
does not treat persisted values as potentially compromised on replay. A finding 
whose precondition is an attacker who can write to the journal or snapshot 
store is `OUT-OF-MODEL: trusted-input`.
+
+This is a trust statement about the **store**, not a licence for the plugin 
SPI: a defect in Pekko's own replay handling is in scope per §5b.4.
+
+**Q6 — Module in/out split (§2 table). ANSWERED *(maintainer)*, except the 
residual split noted below.**
+
+- **`osgi` — answered *(maintainer)*.** It stays **in model**: security 
reports against it are accepted. It is a barely used feature, and the project 
may in future remove it rather than carry the maintenance overhead — but while 
it ships it is supported, and a finding in it is not `OUT-OF-MODEL: 
unsupported-component`. If it is removed, §12 applies.
+- **`kubernetes/` — answered *(maintainer)*.** It is test-cluster provisioning 
tooling — four files (`setup.sh`, `create-cluster-gke.sh`, 
`test-node-base.yaml`, `.gitignore`), not a build module. It is correctly out 
of scope with the build and docs sources. Kubernetes *functionality* — 
discovery, bootstrap, lease — is not in this repository at all: it lives in 
**Apache Pekko Management**. That is a different repository, not a different 
security process: reports are made and triaged exact [...]
+- The rest of the in/out split shown in §2 remains the ASF Security team's 
proposal *(inferred)*.
+
+**Q7 — The negative claims in §5. ANSWERED.**
+
+- **Environment variables — *(maintainer)*.** Read during configuration 
startup, never modified. §5 states the mechanism.
+- **Sockets — answered.** The original claim was wrong and is corrected in §2, 
§5 and §6: `pekko-actor` ships `org.apache.pekko.io.Tcp`/`Udp` and 
`pekko-stream` ships `Tcp`, `TLS` and `FileIO`, all of which bind or open only 
on an explicit application call.
+- **Signal handlers and child processes — *(maintainer)*.** None: no 
`sun.misc.Signal`, `SignalHandler`, `Runtime.exec` or `ProcessBuilder` in the 
main sources. **Two** JVM shutdown hooks are registered — `CoordinatedShutdown` 
and, when `pekko.jvm-shutdown-hooks` is on (the shipped default), Artery's 
transport — both disclosed in §5; a shutdown hook is not a signal handler.
+- **stdout/stderr — *(maintainer)*.** Logging goes to the configured logger. 
`StandardOutLogger` prints to stdout during early startup, bounded by 
`pekko.stdout-loglevel` (default `WARNING`), disclosed in §5.
+- **Process-global state — *(maintainer)*.** Not mutated at initialization: no 
`System.setProperty`, `Locale.setDefault` or `TimeZone.setDefault`.
+
+**Q8 — Resource guarantees. ANSWERED *(maintainer)*.** **CPU cost is not in 
itself a security concern.** A report that Pekko does more work than a reporter 
expected, or that load from a peer degrades throughput, is a performance issue 
and is handled as one — not a vulnerability. Constant-factor overhead 
proportional to a message is expected and is never a finding.
+
+The exception is an input that is **completely out of the ordinary**: one 
crafted so that cost explodes disproportionately to it — an 
algorithmic-complexity path where work grows super-linearly in the size or 
structure of a single message. That is a defect per §5b.4, and where it is 
reachable from an adversary §7 admits, it is `VALID`.
+
+The test is **disproportionate cost**, not the appearance of the input; an 
input that looks out of the ordinary is the usual sign of such a path rather 
than a separate condition to meet. Two notes on applying it:
+
+- Artery already bounds message size by configuration — `maximum-frame-size` 
defaults to 256 KiB and `maximum-large-frame-size` to 2 MiB 
(`remote/src/main/resources/reference.conf`) — so the input is bounded on the 
remoting path.
+- Exhaustion driven by message **volume** from an authenticated peer is not 
covered, and remains subject to §7, under which such a peer is trusted.
+- **Disproportionate cost includes node loss.** Framing failures on one Artery 
TCP connection tear down only that connection, but the decoder and deserializer 
stages downstream of the `MergeHub` are shared by every inbound connection. 
They drop messages on `NonFatal` errors; a **fatal** JVM error there 
(`OutOfMemoryError`, `StackOverflowError`) fails the shared inbound stream, and 
`ArteryTransport.attachInboundStreamRestart` terminates the whole `ActorSystem` 
after `inbound-max-restarts` [...]
+
+**Q9 — Byzantine generalisation. ANSWERED *(maintainer)*.** **Answer:** it 
holds for all of them. No subsystem — cluster membership, sharding, singleton 
or `distributed-data` — claims resilience against a misbehaving member, and 
Pekko has no guarantee of being able to recognise a compromised node at all. 
Failure detection is heartbeat-based and answers "is this member responding?", 
not "is this member honest".
+
+Consequently a finding whose precondition is "a cluster member misbehaves" is 
`OUT-OF-MODEL: adversary-not-in-scope`, and so is one that assumes Pekko should 
have detected the compromise.
+
+**Q10 — Coexistence. ANSWERED *(maintainer)*.** Three documents carry security 
information, each canonical for one thing:
+
+| Document | Canonical for | Reached by |
+| --- | --- | --- |
+| [`SECURITY.md`](SECURITY.md) | **The reporting policy.** The strongest and 
canonical statement of how to report and what the project undertakes | Anyone 
arriving via the repository, and every other document |
+| `THREAT_MODEL.md` (this document) | **Scope** — what is and is not a 
vulnerability, and how a report is triaged | Reporters, triagers, scanning 
tools |
+| `docs/src/main/paradox/security/index.md` | Security announcements, and the 
documentation-site index of security material | Readers of the documentation 
site |
+
+Every other document **links** to `SECURITY.md` for the reporting policy and 
to this document for scope, rather than restating either. A change to the 
policy is therefore made in one place. Where another document currently carries 
a reporting statement that `SECURITY.md` does not, that statement is referred 
to the maintainers for a decision before it is either promoted into 
`SECURITY.md` or dropped — it is not silently migrated.
+
+**Q11 — The §3/§7 boundary non-goals. ANSWERED *(maintainer)*.** Confirmed:
+
+- Pekko is **not a sandbox** and **not an authorization framework** (§3).
+- Out of the adversary model: an attacker with code execution in the embedding 
JVM, a hostile classpath, and side-channel observers — Pekko makes no timing or 
memory-access guarantees of its own. TLS is delegated to the JDK's JSSE, whose 
own guarantees are unaffected by this disclaimer.
+- **In** the adversary model: attacker-influenced *message content* arriving 
by the ordinary path, since that is how application data travels. This covers 
defects in **Pekko's own handling** of that content — a serializer, codec or 
framing defect reachable from a well-formed message is `VALID` per §5b.4. It 
does not extend to the application's interpretation of the content, which is 
the application's responsibility per §6.
+
+**Q12 — Clock assumptions. ANSWERED.** **Pekko has no code that validates or 
cross-checks another node's clock**, and almost nothing that reads one. 
Verified:
+
+- **Failure detection uses the local clock only.** `PhiAccrualFailureDetector` 
records arrival times through its own `clock()` 
(`remote/.../FailureDetector.scala:60`, used at 
`PhiAccrualFailureDetector.scala:144` and `:188`). No remote timestamp enters 
it, so a peer misreporting its clock cannot influence reachability.
+- **The heartbeat timestamp is the sender's own, round-tripped.** `Heartbeat` 
carries `System.nanoTime()` taken by the sender 
(`cluster/.../ClusterHeartbeat.scala:130`); the receiver echoes it back 
verbatim in `HeartbeatRsp` (`:58`); the original sender subtracts it from its 
own clock (`:251`). No node ever interprets another node's clock value, and the 
result feeds only a debug log guarded by `verboseHeartbeat` — a peer echoing a 
false value changes nothing else.
+- **Gossip ordering is logical, not wall-clock.** `VectorClock` is a per-node 
counter incremented on update (`cluster/.../VectorClock.scala:90-92`).
+- **One exception, and it is documented.** `LWWRegister` and `LWWMap` in 
`distributed-data` default to `System.currentTimeMillis()` and merge by highest 
timestamp, so they *do* compare wall-clock values written on different nodes. 
The class states the constraint: *"Merge takes the register with highest 
timestamp. Note that this relies on synchronized clocks. `LWWRegister` should 
only be used when the choice of value is not important for concurrent updates 
occurring within the clock skew" [...]
+
+**Disposition.** Pekko makes no claim against adversarial clock manipulation 
on a cluster member: such a member is misbehaving, and Q9 places it out of 
model. A report that LWW data diverges under clock skew is `BY-DESIGN: 
property-disclaimed` — the constraint is stated on the type itself.
+
+**Q13 — Classic remoting's place in the supported surface. ANSWERED 
*(maintainer)*.** **Answer:** classic remoting is still **supported**. 
Deprecation is not desupport: security reports against it are accepted on 
exactly the same terms as artery. Configuration is the operator's, per §5b — 
its defaults are compatibility choices and a request to tighten one is a change 
request. Bugs in the implementation will be looked at, per §5b.4.
+
+Its settings in §5a therefore follow the same rulings as their artery 
equivalents: `pekko.remote.classic.untrusted-mode` and 
`trusted-selection-paths` follow Q4, and `netty.tcp.enable-ssl` follows Q1.
+
+---
+
+## §15 Appendix — back-map from existing docs
+
+Proof that nothing the project already asserts has been dropped or weakened.
+
+| Existing statement | Source | Lands in |
+| --- | --- | --- |
+| Do not expose an `ActorSystem` to an untrusted network; firewall it | 
`remote-security.md` | §4, §10.1 |
+| TLS with mutual authentication if network protection is insufficient | 
`remote-security.md` | §5a, §8 P6, §10.2 |
+| Nodes should only be reachable from the adjacent network | 
`remote-security.md` | §5 |
+| A compromised node's PKI-tree certificate grants cluster access | 
`remote-security.md` | §5, §7, §9 |
+| Mutual authentication on by default | `remote-security.md` | §8 P6 |
+| Hostname verification recommended on | `remote-security.md` | §5a, §9, §11 |
+| All cluster nodes must be trusted equally | `remote-security.md` | §3, §7, 
§9 |
+| A connected system may send any message to any actor | `remote-security.md` 
| §4, §7, §9 |
+| Untrusted mode does not give full protection | `remote-security.md` | §9, 
§14 Q4 |
+| Untrusted mode blocks system messages, `PossiblyHarmful`, selections | 
`remote-security.md` | §8 P5 |
+| Service mesh is not a replacement | `remote-security.md` | §9 |
+| SHA1PRNG / `urandom` trade-off | `remote-security.md` | §5, §11a |
+| Java serialization disabled by default; discouraged in production | 
`serialization.md` | §5a, §8 P1, §10.3 |
+| `SECURITY`-marked log entries indicate prevented attacks | 
`serialization.md` | §8 P2, §10.4 |
+| Remote deployment is not remote code loading | `remoting.md` | §8 P3, §11a |
+| Remote deployment allow list restricts deployable classes | `remoting.md` | 
§5a, §8 P4 |
+| `serialize-messages` / `serialize-creators` are "only intended for testing" 
| `actor` `reference.conf` | §5a |
+| Report vulnerabilities privately per ASF guidelines; coordinate disclosure 
with upstream maintainers | `security/index.md` | `SECURITY.md`, §1 |
diff --git a/docs/src/main/paradox/security/index.md 
b/docs/src/main/paradox/security/index.md
index dff04551ad..95da98c0b0 100644
--- a/docs/src/main/paradox/security/index.md
+++ b/docs/src/main/paradox/security/index.md
@@ -10,7 +10,9 @@ This mailing list also has announcements of releases for 
Apache projects.
 
 ## Reporting Vulnerabilities
 
-We strongly encourage people to report such problems to our private security 
mailing list first, before disclosing them in a public forum.
+We strongly encourage people to report such problems privately to the Apache 
Security team at
+`[email protected]` first, before disclosing them in a public forum. Apache 
Pekko does not
+operate a separate project security list.
 
 Please follow the [guidelines](https://www.apache.org/security/) laid down by 
the Apache Security team.
 
@@ -23,3 +25,5 @@ first. We will coordinate responsible disclosure with the 
affected upstream main
  * @ref:[Java Serialization](../serialization.md#java-serialization)
  * @ref:[Remote deployment allow 
list](../remoting.md#remote-deployment-allow-list)
  * @ref:[Remote Security](../remote-security.md)
+ * [Security Policy](https://github.com/apache/pekko/blob/main/SECURITY.md)
+ * [Threat Model](https://github.com/apache/pekko/blob/main/THREAT_MODEL.md)


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to