Core asks the field access resolver for its fingerprint before every field
decision (FieldAccessHelper.getDecision), that is once per field of every
record of a result and of every table in the metaData document. Each call
recomputed it: a JSON key, a permission lookup for every table, another JSON
document and a digest. Core also asks for the retention rules of a field on
every audited field and every filter criterion, and each question was a
query.
- getAccessFingerprint keeps its value in the request context (QContext) and
returns it while the session, its permission set, the channel, the policy
and classification versions are unchanged and no active break-glass grant
has expired. Nothing outlives the request.
- RetentionRules.active answers from the active rules the runtime read once
(SecuredDataRuntime.activeRetentionRules). A rule write clears them after
commit; a thread that edits rules inside a transaction reads its own edits.
- Policy, classification and retention changes are announced on QQQ's cluster
bus (ClusterCaches, memo-invalidate), so every node reads again. Before,
the versions were node-local and another node kept old rules until restart.
- No node uses rule rows older than 30 seconds without reading them again
(the safety net for a lost event).
- A read that fails throws and replaces nothing: never an empty rule list.
Measured in one JVM with GG Forge's composed instance (222 tables, secured
data and collaboration wired, in-memory backend, platform administrator),
released 0.1.0-RC.4 against this change, same machine, back to back:
metaData request 866.6 ms to 125.3 ms (930 MB to 74 MB allocated, 96 to 34
queries); a 300-row list 1,566 ms to 24 ms; a 27-row list 174 to 230 ms down
to 13 to 17 ms; queries per list 23 to 8.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Replace the platform-ci release callers with one caller of
KofTwentyTwo/ci-workflows maven.yaml (qqq-orb port).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The worker thread's task and the JVM shutdown hook referenced the logger
(and through it the QInstance) strongly for the life of the JVM, so every
instance a host or test built and dropped stayed in memory (kof22's suite
ran out of heap in CI). Both now hold the logger weakly: a dropped
instance can be collected and its worker stops at the next tick.
- withRegisterShutdownHook(false) for hosts that close the runtime
themselves (default true, unchanged behaviour).
- isWorkerRunning() for hosts and tests.
- README and CHANGELOG (also notes #18).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- SecuredDataClassifications.effective looked fields up with
QTableMetaData.getField, which throws for a virtual (computed) field, so
any user read of a table with one failed (kof22's permalink sitePath and
siteUrl on every content table, and API validation). Virtual fields now
take the table classification.
- The access-log worker opened a QContext on every tick, even with an empty
queue. QContext validates an unvalidated instance, and the worker starts
while the host is still building it, so the first tick validated (and
enriched) a half-built instance on another thread; one failure cancelled
every later tick for good. Idle ticks now return before opening a
context, and a failed tick is logged and retried.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
QRun-IO/.github#2 adds a tag-and-release job that signs vX with the
QRUN_TAG_SIGNING_KEY secret after Maven Central confirms the artifact.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The canary was 16 random letters g-v and any 5-letter window counted as a
leak, so ordinary JSON words built from those letters (options, joins)
matched by chance and RetentionProofTest failed intermittently. The canary
now has 24 random letters and a leak is the whole marker or a 12-letter
window of its random part (16^12 values): chance matches are impossible,
truncated or re-encoded fragments of a real leak are still caught. A new
test pins both directions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Regenerated against UI API 1.6.0: every operation has an operationId and
the standard error responses (BasicErrorResponseV1: error, code, message,
fieldErrors, traceId); the extension's own error schema is kept but
deprecated; hand-written error bodies go through QJavalinUtils so they carry
code/message/traceId. Extension MINOR bumped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RC.7 ships field access (#1172) and audit value protection (#1182), so the
qbit builds on the released core with no stand-ins. Also repairs a
non-UTF-8 character in the changelog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
QQQ #1172 and #1182 are released, so the standin package and the
reflective core bridges go; the resolver, key source and audit protector
register through core metadata, and the core-dependent tests run
unconditionally.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves qqq-orb@0.6.8 to qqq-orb@0.6.9, which passes --update-snapshots so cached SNAPSHOT dependencies are re-checked on every build (QRun-IO/qqq-orb#22).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves the pin to QQQ 4.1.0-RC.6 and removes the stand-ins RC.6 provides
(classification, client channels, table features, the UI API extension
point). Adds retention rules, wipe markers, legal holds, wipe runs and the
per-record-per-field audit key table; the wipe process (null with marker,
overwrite, keyed hash, delete, archive then wipe) runs in one transaction
per batch, destroying the audit keys with the field updates; the audit
protector and key source (AES-GCM wrapped DEKs, AAD-bound); the backups
warning; canary and wipe-proof tests.
Fixes a key-source bug that zeroed each DEK after returning it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One Secured Data home: KPIs, recent reveals, top revealers and reasons,
break-glass events, policy and classification changes and anomaly flags,
as full-width tables. Anomaly rules (3x daily median, distinct records per
hour, exact-match volume, break-glass) raise secured_data.anomaly and flag
the log row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reveal service (options, reason validation, daily cap, grants, audit),
break-glass start/end/expiry with alerts and admin notification, status,
the secured-data 1.0.0 UI API extension (compiled under the uiapi profile
until RC.6) and a contingent field picker for classification rows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tables secured_data_classification, _policy, _reveal_grant, _break_glass
and _access_log; SecuredDataClassifications; the tier resolver with
break-glass, table features, policy rows, channel caps and code defaults;
the asynchronous access log; enumeration guardrails (rate limit, miss and
distinct-value alerts, minimum entropy). Builds on QQQ RC.5 with marked
stand-ins for the unreleased fieldaccess API and a reflective bridge.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replaces the template example with the qbit-secured-data skeleton:
pom on qbit-build-parent with checkstyle and coverage gates, CircleCI,
release workflow, governance files and a minimal producer and config.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>