feat(settings): record every authorization decision to a hash-chained local log - #2216
Merged
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every database operation TablePro performs passes one authorization step, including the ones the AI assistant and the 19 MCP tools ask for. This records each decision, allowed or refused, to a local hash-chained log.
Not 16 call sites
The obvious reading of this task is that
OperationDecision.denied(reason:)carries no connection, kind or timestamp, so logging denials means widening it across everyauthorizecall site, which under the atomic-API rule is one large commit.That is not needed.
DefaultExecutionGate.authorizealready holds the wholeOperationRequestand produces all seven outcomes itself, so everything a record needs is in scope inside that one function. The gate is precisely where the request and the decision exist at the same moment; reconstructing either at the call sites is what would have been expensive.authorizeis now a wrapper over a privatedecide, which keeps the seven returns and gains none of the logging. One write point instead of seven, so areturnadded later cannot quietly skip the log.What a record holds
Time, connection, operation kind, who asked, whether it was a write, and the outcome.
The caller matters most.
OperationCalleralready distinguishesuserInterface,mcpClient,aiAssistant,importPipelineandbackgroundMaintenance, and telling a person clicking Save apart from an agent is the first thing anyone reading an audit trail wants. Only the channel is stored: an MCP client's label and an AI session id are caller-supplied strings and do not belong in an audit record.The statement is stored as a SHA-256 digest, never as text. A query holds customer data, and an audit trail that stored it would be a second copy of the database sitting in Application Support. A test asserts the digest of a query containing an email address does not contain that address.
The honest limit, asserted in a test
Each record carries the previous record's hash, so an edit, a reorder or a deletion breaks the chain and
verify()names the first record that disagrees.It is tamper evident, not tamper proof. Anyone who can write the file can recompute every hash after the record they changed, and truncating the tail leaves a chain that still verifies.
truncationIsNotDetectedasserts exactly that, so nobody later mistakes this for something stronger. Only an append-only store the audited person cannot write would be stronger, and TablePro runs no server. The docs say this in a Warning rather than implying more.Not gated
Deliberately free. The MCP activity log already ships with no
LicenseTiercheck anywhere inTablePro/Core/MCP/, and repo issue #2107's hard rule is that nothing already free moves behind a paywall. Shipping this as a paid tier would have broken that rule for a feature users already have most of.Notes
ExecutionAuditLogis an actor rather than a lock-guarded class: the sequence number and the previous hash have to be read and advanced together, or two concurrent decisions produce two records claiming the same position. A test runs 20 concurrent decisions and asserts 20 distinct sequence numbers and an intact chain.A log file that cannot be decoded is reported through OSLog and left alone rather than replaced with a fresh chain, since silently starting over destroys the evidence.
No UI yet. The log is written and verifiable; presenting and exporting it is separate work.
Verification
buildPASStest13 executed, 13 passedlint TablePro0 violations