dulvac commented on code in PR #3058:
URL: https://github.com/apache/jackrabbit-oak/pull/3058#discussion_r3712063611


##########
oak-doc/src/site/markdown/security/audit-design.md:
##########
@@ -0,0 +1,435 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+
+Audit Pipeline Design
+--------------------------------------------------------------------------------
+
+This document describes the design of Oak's audit pipeline: the SPI surface
+in `oak-core-spi`, the security-domain constants in `oak-security-spi`, the
+pipeline implementation in `oak-core`, OSGi and embedded wiring, and the
+threading and ordering rules the implementation relies on.
+
+For the consumer-facing guide (event model, listener contract, trust model),
+see [Audit SPI](audit.html).
+
+<a name="overview"></a>
+### Overview
+
+The audit pipeline transports structured `AuditEvent`s from producers to
+bundle-registered `AuditEventListener` consumers, gated by a feature toggle
+and a per-domain listener registry.
+
+There are two delivery paths:
+
+- **Commit-attached.** Oak-internal capture sites (e.g. `UserManagerImpl`)
+  call `AuditEvents.record(root, event)`. Events land in a per-session
+  `ThreadLocal` buffer (`AuditBuffer`), and a `NodeStore` `Observer`
+  (`AuditDrainObserver`) drains and dispatches them after the surrounding
+  `Root.commit()` durably persists. At drain time each event is decorated
+  with `commit.sessionId`, `commit.userId`, and `commit.timestamp` payload
+  entries; a failed commit drops the buffer.
+- **Fire-and-forget.** Any OSGi bundle resolves `AuditEventEmitter` via
+  `@Reference` and calls `emit(event)`. The event is dispatched synchronously
+  on the calling thread: no buffering, no commit boundary, no payload
+  decoration. Caller-supplied values for the three reserved `commit.*`
+  attestation keys are stripped before delivery (the trust contract on
+  `AuditEvent#getPayload()` is the normative statement).
+
+Both paths converge on `AuditEventListener.onEvents(List<AuditEvent>)` and
+share one listener registry. Failure isolation is layered: an outer
+`Throwable` barrier in `AuditDrainObserver.contentChanged` keeps audit from
+masquerading as a commit failure, and an inner per-listener `Throwable`
+barrier on both paths keeps one misbehaving listener from stopping the
+others.
+
+Pipeline state is owned by `AuditConfigurationImpl` in `oak-core`, which
+holds the feature toggle, the buffer, the listener registry, the sink
+installed into the `AuditEvents` facade, and the singleton drain observer.
+It is registered as an OSGi service of type `AuditConfiguration`. Audit is a
+top-level Oak concern, not a `SecurityConfiguration`.
+
+<a name="pipeline_diagram"></a>
+### Pipeline diagram
+
+![Audit pipeline](audit-pipeline.png)
+
+The upper row is the commit-attached path, the lower row fire-and-forget.
+They converge on the listener registry, which filters by domain and orders by
+rank before invoking each listener.
+
+On the commit-attached path, `BufferSink.record` gates on the feature toggle
+and on whether any listener is registered for the event's domain, so a
+capture site allocates nothing when audit is off. The observer runs on the
+commit thread once the merge has persisted, drains the buffer for that
+session, and stamps the three `commit.*` keys. On the fire-and-forget path,
+`BufferSink.dispatch` applies the same toggle and listener gates, strips
+caller-supplied `commit.*` values, and dispatches inline.
+
+The short-circuit order in the observer, and the exception barriers on both
+paths, are described under Implementation below.
+
+<a name="commit_flow"></a>
+### Commit flow
+
+![Commit flow](audit-commit-flow.png)
+
+The observer fires synchronously on the commit thread, after durable
+persistence and before `store.merge` returns. That is the property the
+per-session buffer depends on: the drain has to happen on the thread that
+filled it. Every production `NodeStore` notifies observers that way, though
+not all by the same route. The document and composite stores dispatch
+through `ChangeDispatcher`; `MemoryNodeStore` iterates its registered
+observers directly from `setRoot`. Either way the notification completes
+before `merge` returns.
+
+Two segment-store configurations are worth knowing about, because in both the

Review Comment:
   fair. the key constants are package-private in oak-core, so a listener has 
no choice but to hardcode the string. I'll add a static predicate plus public 
key constants to the SPI, and cut the doc down to naming it instead of 
describing the check.
   But the predicate can only ever be "these keys are present", since Oak 
doesn't sign anything so I'll name it for what it checks rather than implying a 
stronger guarantee. And it belongs in `oak-core-spi` next to `AuditEvent,` so 
listeners keep their single-module dependency. The API change lands in the 
implementation PR (#3059) and I'll update this page to match.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to