feat(ipc): .ipc.on.* connection hooks#216
Merged
Merged
Conversation
Replaces v1's two-letter port hooks with five descriptive hooks under
.ipc.on.* plus a read-only .ipc.handle:
.ipc.on.open {[h] ...} inbound conn fully handshaked
.ipc.on.close {[h] ...} inbound conn about to close
.ipc.on.sync {[m] ...} sync request; return = response
.ipc.on.async {[m] ...} async message; return ignored
.ipc.on.auth {[u; p] ...} narrows -u/-U auth (truthy = accept)
(.ipc.handle) current handle inside any hook, -1 else
Hooks install via plain set / : through a narrow allow-list carved out
of ray_sym_is_reserved — only these five sym ids are user-settable;
everything else under .ipc.* (open/close/send/handle) stays unsettable,
as do all other dotted system namespaces.
Wiring lives in src/core/ipc.c covering both server paths:
- Poll: ipc_read_handshake (no-auth ready), ipc_read_creds
(post-auth ready + on.auth narrow), ipc_on_close,
ipc_read_payload (sync/async dispatch + handle scope).
- Legacy: conn_on_handshake, conn_on_creds, conn_close,
conn_on_payload, with matching semantics.
Lifecycle on.close fires only for connections that completed handshake,
keeping the on.open / on.close pair balanced for the user. Sync hook
errors flow back to the client as the response; async hook errors are
logged and dropped (no wire response on async); on.auth errors are
treated as reject; lifecycle hook errors are logged and swallowed.
Hook lookup goes through env.c's central sym cache (ray_sym_ipc_hook
getter) so a runtime destroy/recreate cycle invalidates the IDs in one
place and the IPC layer doesn't carry its own duplicate cache.
Tests: ipc/hooks_lifecycle drives an end-to-end open → sync → close
round-trip and reads back per-hook counters and .ipc.handle through the
global env. ipc/hooks_auth_narrow exercises a hook that selectively
rejects one username on top of -u password auth. rfl/system/
reserved_namespace gets positive cases for set/let on the five hook
names and negative cases ensuring the rest of .ipc.* and the new
.ipc.handle stay reserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New 'Connection Hooks' section in ipc.html with the hook table, install/clear semantics, the carve-out note, and per-hook error behaviour. Five new rows in reference.html covering .ipc.handle and the five hook slots. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
990938e to
659e94d
Compare
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.
Replaces v1's two-letter port hooks with five descriptive hooks under
.ipc.on.*plus a read-only.ipc.handle:.ipc.on.open(fn [h] ...)-u/-U), before first header read..ipc.on.close(fn [h] ...)on.open..ipc.on.sync(fn [m] ...).ipc.on.async(fn [m] ...).ipc.on.auth(fn [u p] ...)-u/-Uauth. Truthy = accept, falsy = reject. Can only narrow..ipc.handle(.ipc.handle)-1outside.Install
Plain
set/:, through a narrow allow-list carved out ofray_sym_is_reserved:Only these five sym IDs are user-settable. Everything else under
.ipc.*(open,close,send,handle) and every other dotted system namespace stays unsettable. Clearing a hook is(set .ipc.on.X 0)— anything that isn't a callable lambda is treated as "no hook installed".Wiring
src/core/ipc.ccovers both server paths:ipc_read_handshake(no-auth ready),ipc_read_creds(post-auth ready +on.authnarrow),ipc_on_close,ipc_read_payload(sync/async dispatch + handle scope).conn_on_handshake,conn_on_creds,conn_close,conn_on_payload.Lifecycle
on.closefires only for connections that completed handshake, keeping theon.open/on.closepair balanced for the user. Hooks inherit the connection's restricted-mode flag — a.ipc.on.syncinstalled on a-Userver cannot escalate.Sym-cache lifetime
Hook sym IDs live in one place (
g_ipc_hook_syms[]inenv.c, accessed viaray_sym_ipc_hook(idx)) so a runtime destroy/recreate cycle (which tears the sym table down too) invalidates the cache exactly once. The IPC layer doesn't carry its own duplicate cache.Deliberately not pre-interned in
ray_env_init— single-char operator names need the low sym-ID slots so theresolvebuiltin's "slen<2 / operator-char" guard rejects them on small-int columns. Stealing IDs 1-5 for long dotted names silently promoted[1 2 3]columns to SYM (caught byprint_resolve.rfl).Per-hook error handling
on.open/on.closeon.syncevalerror).on.asyncon.auth0x01byte the wrong-password path uses.Tests
ipc/hooks_lifecycle— end-to-end open → sync → close round-trip on the legacy path; reads per-hook counters and.ipc.handleback through the global env.ipc/hooks_auth_narrow— poll-path-userver with an.ipc.on.auththat rejects one username; proves the hook can selectively narrow rather than blanket-reject.rfl/system/reserved_namespace— positive set/let cases for the five hook names + negatives ensuring the rest of.ipc.*and the new.ipc.handlestay reserved.make test: 2847 of 2850 passed, 3 skipped, 0 failed (3 skipped pre-existed).Docs
New "Connection Hooks" section in
website/docs/ipc.htmlplus rows inwebsite/docs/reference.html.🤖 Generated with Claude Code