RPC-like communication between yunos over the network. A local gobj subscribes to a remote service as if it were local.
Source: kernel/c/root-linux/src/c_ievent_cli.c, c_ievent_srv.c
C_IEVENT_CLI¶
Inter-event client — connects to a remote yuno and simulates its service as a local gobj. Handles identity-card exchange, authentication, and subscription management.
| Property | Value |
|---|---|
| States | ST_STOPPED, ST_DISCONNECTED, ST_WAIT_CONNECTED, ST_WAIT_IDENTITY_CARD, ST_SESSION, ST_SUBSCRIBED |
| Input events | EV_SEND_MESSAGE, EV_ON_MESSAGE, EV_ON_OPEN, EV_ON_CLOSE, EV_TIMEOUT, EV_DROP, EV_STOPPED |
| Output events | EV_ON_MESSAGE, EV_ON_OPEN, EV_ON_CLOSE |
Key attributes¶
| Attribute | Type | Description |
|---|---|---|
wanted_yuno_role | string | Role of the remote yuno to connect to. |
wanted_yuno_name | string | Name of the remote yuno. |
wanted_yuno_service | string | Service name on the remote yuno. |
url | string | Connection URL. |
jwt | string | JSON Web Token for authentication. |
timeout_idack | integer | Identity-card ack timeout in seconds. |
Stopping with remote subscriptions open¶
gobj_stop() closes the transport, but the FSM stays in ST_SESSION until
the close arrives, so EV_ON_CLOSE is still published. A subscription added
or withdrawn in that window is not sent to the peer. The peer’s
C_IEVENT_SRV drops every subscription of the channel when the channel
closes, and an added subscription is sent at the next open. Up to 7.25.4 the
__unsubscribing__ frame went down to the stopping transport, and
C_WEBSOCKET or C_TCP logged “Event NOT DEFINED in state”. Example:
gobj_stop_tree(priv->gobj_remote); // the transport starts to close
gobj_unsubscribe_event(priv->gobj_remote, EV_X, 0, gobj); // not sent, and not an errorC_IEVENT_SRV¶
Inter-event server — entry gate for authenticated service access. Manages incoming connections, routes inter-event messages to local services, and handles WebSocket upgrade.
| Property | Value |
|---|---|
| States | ST_STOPPED, ST_DISCONNECTED, ST_WAIT_IDENTITY_CARD, ST_SESSION |
| Input events | EV_ON_MESSAGE, EV_ON_OPEN, EV_ON_CLOSE, EV_TIMEOUT, EV_DROP, EV_STOPPED |
| Output events | EV_ON_MESSAGE, EV_ON_OPEN, EV_ON_CLOSE |
Key attributes¶
| Attribute | Type | Description |
|---|---|---|
__username__ | string | Authenticated username (read-only). |
__session_id__ | string | Session identifier (read-only). |
jwt_payload | json | Decoded JWT payload. |
client_yuno_role | string | Role of the connected client yuno. |
client_yuno_name | string | Name of the connected client yuno. |
this_service | string | Local service name this gate serves. |
authenticated | bool | Whether the connection is authenticated. |
max_subscriptions | integer | Subscriptions a peer may hold on the channel (default 5000, 0 no limit). See What a peer may hold. |
max_subscription_size | integer | Bytes, as compact json, of the __filter__, of the __global__ and of the routing back (__md_iev__) of a peer’s subscription, each one (default 16384, 0 no limit). |
max_pre_session_frame | integer | Bytes of a frame received before the session (default 65536, 0 no limit). See below. |
Lifecycle of a channel¶
The tree of a channel (C_CHANNEL → C_IEVENT_SRV → C_WEBSOCKET or
C_PROT_TCP4H → C_TCP) belongs to its gate, not to one connection.
gobj_start_tree() of the gate starts it. When the peer leaves, only its
C_TCP stops: the protocol gobj stays running and serves the next connection
that the channel accepts.
When the gate stops, all of the tree stops: each layer stops the one below
it, and C_IEVENT_SRV stops its protocol gobj since 7.25.5. Up to 7.25.4 a
gate stopped with a plain gobj_stop() (the way the yuno stops an
autostart service) left each C_WEBSOCKET or C_PROT_TCP4H running, and the
yuno exited with “Destroying a RUNNING gobj”. Only an owner that called
gobj_stop_tree() on the gate stopped it all.
A channel that is disabled and enabled again (the C_IOGATE commands
disable-channel and enable-channel) comes back whole: C_CHANNEL
restarts its tree, and C_IEVENT_SRV starts its protocol gobj in its start,
the mirror of its stop. The transport C_TCP is manual start: C_TCP_S
starts it for each connection it accepts.
ycommand -c 'command-yuno id=<id> service=__input_side__ command=disable-channel channel_name=^input-1$'
ycommand -c 'command-yuno id=<id> service=__input_side__ command=enable-channel channel_name=^input-1$'channel_name is a regular expression. It selects the channels whose name
it matches: ^input-1$ on a gate with input-1 and input-2 selects only
input-1. One that matches no channel selects nothing, and the command
answers with the header of the view only. (Up to 7.25.4 each of the six
commands looped for ever on the first channel that did not match, and
blocked the yuno.) A text that is not a valid regular expression is refused with -1,
for example channel_name=input-[ answers “<role^name>: channel_name is not
a valid regular expression: ‘input-[’” (up to 7.25.4: “regcomp() failed”).
Start and stop the gate as a pair. Either declare it "autostart": true in
the config and let the yuno start and stop it, or do both in the owner:
PRIVATE int mt_play(hgobj gobj)
{
PRIVATE_DATA *priv = gobj_priv_data(gobj);
priv->gobj_input_side = gobj_find_service("__input_side__", TRUE);
gobj_subscribe_event(priv->gobj_input_side, 0, 0, gobj);
gobj_start_tree(priv->gobj_input_side);
return 0;
}
PRIVATE int mt_pause(hgobj gobj)
{
PRIVATE_DATA *priv = gobj_priv_data(gobj);
gobj_stop_tree(priv->gobj_input_side);
return 0;
}Subscription authz¶
A peer subscribes to an event of a local service with a __subscribing__
message. C_IEVENT_SRV accepts it only for a public output event of a
service the channel may reach. Since 7.25.5, when the yuno sets
enable_subscription_authz (off by default) and the event is flagged
EVF_AUTHZ_SUBSCRIBE, the channel’s user also needs the publisher’s
permission aliased __subscribe_event__ — read for the EV_TREEDB_NODE_*
feed of a treedb and for the EV_TRANGER_RECORD_ADDED feed of a
C_TRANGER — or the global __subscribe_event__. A refused subscription is
logged (“No permission to subscribe event”) and not made; the channel stays
open. The refusal is logged once per service and event on a channel: a peer
that repeats it does not write a log line per frame. When the peer withdraws a
refused subscription later, that is logged at info level (“its subscription
was refused”). A withdrawal that matches no subscription of the channel, and
was not refused, is a warning (“UNSUBSCRIBING event matches no subscription
of this channel”), with the frame cut to 256 bytes. Both are the peer’s to
repeat, so each is written at most once per 10 s per channel, with the count
of the ones not written (suppressed). Up to 7.25.4 each frame wrote its
line, and the no-match warning the whole frame, as big as the peer made it.
Example: a yuno that enforces the treedb feed permission, in its config:
{
"yuno": {
"enable_subscription_authz": true
}
}Details, and how a gclass declares a guarded event:
YUNO_AUTH.md §4.6.
What a peer may put in a subscription¶
The kw of a __subscribing__ message can carry __config__, __global__
and __filter__, as a local gobj_subscribe_event() does. C_IEVENT_SRV
builds the subscription from those three, and keeps of them only what a peer
may set (since 7.25.5):
| Key | What is kept |
|---|---|
__filter__ | All of it. It decides only the peer’s own deliveries. |
__config__ | The keys in the list below. |
__global__ | The keys of the peer’s own: not one that starts with _ (the framework’s: __md_iev__, __md_yuno__, __service__, ...), and not gbuffer, the binary field of every kw. They come back to the peer in every event of this subscription, and to nobody else. |
__local__ | Nothing. The one __local__ of a remote subscription is the reference to the channel, set by C_IEVENT_SRV. |
| any other key | Nothing. |
What is dropped is logged as a warning, “SUBSCRIBING keys a peer may not
set, ignored”, at most once per 10 s per channel. A __filter__ or a
__global__ bigger than max_subscription_size refuses the subscription
(“SUBSCRIBING refused, bigger than max_subscription_size”, same pace).
To route every event of the subscription back to the peer, the gate adds
its own back-metadata to the stored __global__: __md_yuno__, and a
__md_iev__ built from the TOP record of the frame’s routing stack (the hop
of this peer, with the gate’s stamps), reversed. Nothing else of the frame’s
__md_iev__ is kept: no key of the peer’s own and no deeper hop. That
routing is measured against max_subscription_size too, after it is built:
a bigger one (a record with long strings) refuses the subscription,
“SUBSCRIBING refused, its routing is bigger than max_subscription_size”,
at most once per 10 s. Up to 7.25.4 the frame’s whole __md_iev__ was
copied, keys of the peer’s own included, and nothing of a subscription was
measured: a peer could store as much as a frame holds in each subscription,
with no cap on their number, and got it back with every event.
Up to 7.25.4 only __config__ was filtered, and the publish shared ONE kw
with every subscriber, so a peer’s subscription changed the event of every
subscriber after it: its __global__ forged keys of the event (a
topic_name, a node), its __local__ removed them, the filters of the
later subscribers were evaluated on the forged kw, and the back-metadata of
the gate (__md_iev__, with the peer’s user name and channel) reached a
local subscriber. A gbuffer in the peer’s __global__ was taken for a
pointer when the event was serialized back to it, and crashed the yuno. Now
gobj_publish_event() gives a subscription with a __local__ or a
__global__ a twin of the kw of its own
(gobj_publish_event()), and C_IEVENT_SRV changes a
kw that somebody else holds only on a copy.
Of __config__ the list of keys a peer may set has one key:
| Key | Meaning |
|---|---|
__first_shot__ | Read by the publisher in its mt_subscription_added(): false asks it not to send its current state when the subscription is made. |
Any other key is removed before the subscription is made, and logged as a warning (“SUBSCRIBING config keys a peer may not set, ignored”). Three of those keys change how the framework delivers the event, and a peer must never have them:
__hard_subscription__makes the subscription survive the close of the channel. The channel is static, so it went to the next user of it, who got the feed without asking and past the subscription authz.__own_event__stops the publish loop when the delivery to this subscription fails. With the channel closed every delivery fails, and every subscriber after it lost the event.__rename_event_name__delivers the event toC_IEVENT_SRVunder another name, one of its own inputs among them.
Up to 7.25.4 the peer’s __config__ went through whole. Also since 7.25.5,
the close of a channel removes every subscription it made with force, hard
or not.
Example: a SPA subscribes without the first shot, and that key arrives:
gobj_subscribe_event(gobj_remote, "EV_REALTIME_TRACK", {
__config__: {__first_shot__: false},
__filter__: {id: device_id}
}, gobj);A key that a publisher reads from a peer is added to the list in
c_ievent_srv.c (peer_subscription_config_keys), and documented here.
A peer withdraws a subscription with an __unsubscribing__ message that
repeats what it subscribed. The kw is filtered the same way, and compared
with what the peer SENT: the __global__ that C_IEVENT_SRV stores carries
its own back-metadata too, which the peer never repeats. Up to 7.25.4 it was
compared whole, and a subscription with a __global__ could not be withdrawn
until the channel closed.
Example: a C client tags the events of its subscription, and withdraws it:
json_t *kw = json_pack("{s:{s:s}, s:{s:s}}",
"__filter__", "topic_name", "devices",
"__global__", "tag", "devices_view" // comes back in each event
);
gobj_subscribe_event(gobj_remote, EV_REALTIME_TRACK, json_incref(kw), gobj);
gobj_unsubscribe_event(gobj_remote, EV_REALTIME_TRACK, kw, gobj); // the same kwWhat a peer may hold¶
Every subscription costs a scan of the publisher’s subscriptions when it is
made, and one more on every publish of its event. A peer could make them
without end: 20000 subscriptions of one peer blocked the event
loop for 80 s, and every later publish took 13 ms. So a channel holds at most
max_subscriptions of its peer. Beyond it a subscription is refused, logged
once (“SUBSCRIBING refused, the peer holds max_subscriptions”), and not
again until the peer is under the cap.
A subscription that repeats one the peer holds takes no room:
the SAME subscription (the same
__filter__,__global__,__local__and__config__) is left as it is. It is not made again: nomt_subscription_deleted()/mt_subscription_added(), and no second__first_shot__.one that overrides a subscription it holds (a match of
gobj_subscribe_event(): the new one has no__filter__, say) takes that one out and is made.
Both are logged as a warning, at most once per 10 s per channel
(“SUBSCRIBING repeated, the one held is kept”, “SUBSCRIBING overrides one
held, it is replaced”). A client does not send either: C_IEVENT_CLI
withdraws the subscription it replaces first. Up to 7.25.4 every repeated
frame went to gobj_subscribe_event(), which deleted the subscription, made
it again, and logged a warning with a stack trace and the whole kw.
A gate whose peers subscribe per device (two subscriptions per device, in the
SPAs of hidraulia) raises the cap in the kw of the C_IEVENT_SRV of its
channel tree:
{
"name": "input-(^^__range__^^)",
"gclass": "C_IEVENT_SRV",
"kw": {
"max_subscriptions": 20000
}
}What the gate stamps¶
The gate writes who sent a message into the kw it hands on, and overwrites
whatever the peer put there under the same key. In a command, a stats
request and an event, __username__ is the channel’s authenticated user
(set by C_AUTHZ), whatever the peer sent. In the routing stack of an event
(__md_iev__), __username__, input_channel and input_service are the
gate’s. The command parser’s authz check reads that __username__.
Example: a peer sends list-yunos with a __username__ of its own in the kw
of the command:
{"__username__": "admin"}The service gets "__username__": "bob" when the channel’s user is bob.
Up to 7.25.4 kw_set_dict_value() kept a key that
already existed, so the peer’s admin reached the service.
A frame the gate cannot route¶
Every frame of a session carries its routing: __md_iev__ with the ievent
stack, whose top record says where the frame comes from (src_yuno,
src_role, src_service, strings) and, if it says, where it goes
(dst_yuno, dst_role, dst_service, strings). C_IEVENT_CLI (C and JS)
pushes it on every request, and an answer copies the one of its request.
A well-formed routing (__msg_type__ is optional, a string when present):
"__md_iev__": {
"ievent_gate_stack": [
{
"src_yuno": "", "src_role": "ycommand", "src_service": "ycommand",
"dst_yuno": "", "dst_role": "yuneta_agent", "dst_service": "agent"
}
],
"__msg_type__": "__command__"
}The same frame with "src_role": 7, with an empty ievent_gate_stack, or
with no __md_iev__ at all, has no routing, and its channel is closed.
The gate reads what the peer sent with plain json calls, never with a
kw_get_*() reader that logs a wrong type with a stack. What it refuses,
and how:
| The frame | What the gate does | Log (per channel) |
|---|---|---|
| No routing, or a malformed one | Closes the channel, as for a frame that is not json. It cannot be answered, nor routed back. | “Frame without its routing (__md_iev__ ievent stack), channel closed”, WARNING, MSGSET_PROTOCOL, the kw capped to 256 bytes, at most once per 10 s. |
For another yuno (dst_role, dst_yuno) | Closes the channel. | “It’s not my role, channel closed” / “It’s not my name, channel closed”, WARNING, the routing capped. |
| For a service the channel may not reach | A command or a stats request gets a negative answer and the channel stays; a subscription, a withdrawal or an event closes it. | “event ignored, dst_service not authorized for this channel”, WARNING, MSGSET_AUTH, the kw capped, at most once per 10 s. |
| For a service that does not exist | The same. | “event ignored, service not found”, WARNING, same pace. |
| A subscription or a withdrawal of an event that is not public | Refused; the channel stays. | “SUBSCRIBING event ignored, not PUBLIC...”, WARNING, same pace. |
A command or a stats request that names no command (neither __command__ in the kw nor in its stack), or whose service is not a string | Answered with an error; the channel stays. | “Request without the command or stats it asks, refused”, WARNING, the kw capped, same pace. |
A command or a stats request whose service does not exist, or (stats) is one the channel may not reach | Answered with an error; the channel stays. | “Service not found” / “Not authorized to request stats of a different service”, WARNING, same pace. |
| An event no gclass has as public (any state) | Closes the channel. | “Frame of an event that is not public, refused”, WARNING, MSGSET_PROTOCOL, the name masked and THEN capped to 64 bytes (mask_secrets_inline_capped(): capped first, a token the cap cut showed its first bytes), the kw masked and capped to 256 bytes -- said by iev_create_from_gbuffer(), once per connection. Up to 7.26.9 it was silent there, and the gate traced the whole kw raw (“iev_event NULL”), credentials before the session among it. C_IEVENT_CLI the same. |
EV_REMOTE_LOG: a log line of the peer’s app (gobj-js set_remote_log_functions()), in session | Logged. | “Remote Error”, ERROR, MSGSET_APP, the remote msg capped to 1 KB and masked (mask_secrets_inline()), its whole size and the username; the kw is not dumped. Up to 7.26.9 the text was written whole, and the kw traced raw. |
Each of these is the peer’s to repeat, frame after frame, so none writes a line per frame, none carries a stack trace, and none dumps the whole kw. Up to 7.25.4 a frame without routing logged an error with a stack and the whole kw (the lookup was verbose, “TODO check”) and was processed all the same; a command to a service the channel may not reach logged an error, the whole kw, the authorized services and the routing, for each frame; and a subscription to an event that is not public returned an error without closing the channel, which left it connected and deaf.
tests/c/c_ievent_srv_peer_subs.
What the client refuses from its server¶
C_IEVENT_CLI reads the frames of its server the same way: its faults are the
peer’s, one WARNING each (MSGSET_PROTOCOL, no stack, the names it wrote
masked and capped).
| The frame | What the client does | Log |
|---|---|---|
Any event but EV_IDENTITY_CARD_ACK before the session | Closes the channel. | “event UNKNOWN in not-session state” |
For another yuno (dst_role, dst_yuno) | Closes the channel. | “It’s not my role” / “It’s not my name” |
A subscription or a withdrawal (__subscribing__, __unsubscribing__) | Closes the channel: a client is not subscribed to. | “subscription event ignored, I’m client” / “unsubscription event ignored, I’m client” |
In session, an event of the client itself that is not public (EV_ON_OPEN, EV_ON_CLOSE, EV_DROP...: another gclass, C_PROT_HTTP_SR, declares them public, so the frame reads), or the ACK again | Drops the frame; the channel stays. Up to 7.26.9 EV_ON_OPEN and EV_ON_CLOSE reached the app as the client’s own open and close, with the kw the server chose, and the ACK was sent back to the server. | “event of the transport itself, or out of its place, refused” |
In session, an event of another gclass whose dst_service is no service | Drops it; the channel stays (a view gone with its frames still on the wire). Up to 7.26.9 it was published to the client’s subscribers, with an ERROR and a stack, the name in clear. | “service of the event not found, event dropped” |
In session, an event whose dst_service is a service that does not have it as public | Drops it; the channel stays. Up to 7.26.9 it was published to the client’s subscribers, with no log. | “event not public in its service, event dropped” |
In session, an event with an empty dst_service | Publishes it to the client’s subscribers: it names no destination, so it belongs to whoever subscribed. Up to 7.26.9 also an ERROR with a stack, “service EMPTY”. | None |
In session, an event whose dst_service is the client’s own name (the src_service of its identity card: what the server sends on its own, with no __service__) | Publishes it to the client’s subscribers, as an empty one (7.27.0). The name is compared without case, as gobj_find_service() compares it: a client created as a plain child (no as_service; every client of the tree is a service) does with "CLI" for cli what a service does. | None |
In session, EV_MT_STATS / EV_MT_COMMAND whose service is no service | Answers with an error (“Service not found: ‘...’”); the channel stays. | “Service not found”, MSGSET_PARAMETER, the name masked and capped (7.27.0; up to 7.26.9 in clear) |
tests/c/c_ievent_cli_peer_faults.
The C client and the gobj-js client refuse differently¶
The gobj-js C_IEVENT_CLI (the browser’s) reads its server with the same
severity, one WARNING without a stack and the server’s names masked, but it
does not always do the same with the frame. The differences, checked in
c_ievent_cli.c and gobj-js src/c_ievent_cli.js (7.26.27):
| The frame | C C_IEVENT_CLI | gobj-js C_IEVENT_CLI |
|---|---|---|
| Not json, or not an inter-event | WARNING; closes the channel | WARNING; drops the frame, the channel stays |
| An event no gclass has as public | WARNING; closes the channel | WARNING; drops the frame, the channel stays |
__subscribing__ / __unsubscribing__ | WARNING; closes the channel | WARNING; drops the frame, the channel stays |
dst_service names no service | WARNING; drops it (7.27.0; up to 7.26.9 it published it) | WARNING; drops it |
dst_service is a service that does not have the event as public | WARNING; drops it (7.27.0; up to 7.26.9 it published it, no log) | WARNING; drops it |
What the server sends on its own, with no __service__ (addressed to the src_service of the identity card) | Its card names the transport itself: published to its subscribers | Its card names the transport’s PARENT: delivered to that service if it has the event as public, else dropped with a WARNING |
EV_MT_STATS / EV_MT_COMMAND | Served: the stats or the command of the service named, and the answer | Not served: a browser does not run what its server asks |
How a fault closes the channel (an event before the session, another dst_role / dst_yuno) | EV_DROP to the transport: the TCP connection is cut, with no WebSocket close frame | The WebSocket is closed with code 4002, which EV_ON_CLOSE carries |
Since 7.27.0 the two clients do the same with a dst_service they cannot
deliver to: they drop the event. The C one published it until then; no node
logged it in a week of logs, and no C client of the tree receives anything
that way (each one is the src_service of what it asks, and declares the
answers public). What a server sends on its own is the other case: an app
that sends an event to the C_IEVENT_SRV of a channel, with no __service__
in the kw, addresses it to the service the client’s identity card gave --
the C client’s transport, which publishes it to its subscribers, as an empty
dst_service. Name a service to reach it instead:
gobj_send_event(gobj_channel_srv, EV_ALARM, // to the subscribers of the client's transport
json_pack("{s:s}", "device", "dev1"), gobj);
gobj_send_event(gobj_channel_srv, EV_ALARM, // to the client's service `monitor`, which has EV_ALARM public
json_pack("{s:s, s:s}", "device", "dev1", "__service__", "monitor"), gobj);To an anonymous client (no yuno_name) the gate writes no dst_yuno
(7.27.0): its ^peer-sock name is the gate’s label, and up to 7.26.9 the
client refused it (“It’s not my name”) and the channel was closed. Both keep the session
when an event of the transport itself arrives (“event of the transport
itself, or out of its place”), so a hostile server can make either write one
WARNING per frame.
An identity card the gate refuses¶
Before its session a peer may send two things: its identity card
(EV_IDENTITY_CARD) or EV_GOODBYE. The card carries the same routing as
any frame, and it must name this yuno and one of its services:
{
"jwt": "<token, or empty with the BFF cookie>",
"__md_iev__": {
"ievent_gate_stack": [
{
"src_yuno": "", "src_role": "ycommand", "src_service": "ycommand",
"dst_yuno": "", "dst_role": "yuneta_agent", "dst_service": "agent"
}
],
"__msg_type__": "__identity__"
}
}What is refused, each time, is the peer’s: the channel is closed, and the
gate writes ONE warning (MSGSET_PROTOCOL, peername, the kw capped to 256
bytes in kw and its size in kw_size, no stack), with no credential in it.
kw_size is the size of the kw RECEIVED, as compact json (7.27.0+: it was the
size of the masked, capped copy, and a kw of 900 KB said 101). A frame that is
not json, or not an inter-event, is said as what it is by
iev_create_from_gbuffer() (“frame is not
json”, “Frame is not an inter-event ...”), masked; the gate said “Bad
json” for both up to 7.26.9, with a dump of the bytes raw. Before a session there is no
command table to ask which parameter is SDF_SECRET, so the NAME decides
(is_secret_name(), the one list of the SDK: passw, token, secret,
jwt, api_key, private_key, pin... any case): at any depth of the kw
the value of such a key is shown as ******** (whatever its json type), and in
a string (a command line) the value of such a name=value too; and, under any
key, a value with the shape of a credential -- a JWT, the credential after
Bearer or Basic -- the kernel’s json_mask_secrets(),
the same rule as the ievents traces. A command sent before the card:
kw {"jwt": "eyJ...", "password": "s3cr3t", "kw": {"passw": "x"}, "__command__": "help token=abc",
"note": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4In0.c2ln", "h": "Authorization: Bearer abc"}
log {"jwt":"********","password":"********","kw":{"passw":"********"},"__command__":"help token=********",
"note":"********","h":"Authorization: ********", ...}A command to a channel that is NOT in session (mt_command of C_IEVENT_SRV
or C_IEVENT_CLI: the agent’s command-yuno to a yuno that has just gone) is
refused with “Not in session”, an ERROR that names the command by its first
word, capped to 64 bytes and masked: the line is forwarded whole, so up to
7.26.9 it logged a set-password password=... (and, with the line
password=hunter2 user=x, the first word itself: it is password=********
now). A stats asked out of session (mt_stats) is said the same way, its
name capped to 64 bytes and masked: it was logged whole (7.27.0+).
The ievents and ievents2 traces of C_IEVENT_CLI and C_IEVENT_SRV
(trace_inter_event(), trace_inter_event2()) mask the same way, in both
directions. A command (__command__, v6 or v7) is also masked by the command
table of its destination service when that service is in this yuno -- its
SDF_SECRET parameters, positional ones too -- as the commands trace does
(command_mask_secret_line()). Up to 7.25.20
ievents2 printed a command’s password in clear.
kw {"__command__": "set-user-pwd username=bob password=hunter2", "password": "hunter2"}
log {"event": "EV_MT_COMMAND", "kw": {"__command__": "set-user-pwd username=bob password=********", "password": "********"}}| The card | Log |
|---|---|
| No routing, or a malformed one | “Identity card without its routing (__md_iev__ ievent stack), refused” |
dst_role is not this yuno’s role | “Identity card refused, dst_role NOT MATCH”, with dst_role |
dst_yuno given and not this yuno’s name | “Identity card refused, dst_yuno NOT MATCH”, with dst_yuno |
Empty src_role | “Identity card refused, without yuno role” |
Empty src_service | “Identity card refused, without yuno service” |
dst_service is no service of this yuno | “Identity card refused, dst_service NOT FOUND in this yuno”, with dst_service |
jwt present and not a string | “Identity card refused, its jwt is not a string” |
Any event but the card or EV_GOODBYE | “Event before the identity card, channel closed”, with event |
A frame bigger than max_pre_session_frame | “Frame before the identity card too big, channel closed”, with size and max; the frame is not parsed, and its bytes are not dumped (unparsed they cannot be masked, and a card carries a jwt) |
| An event no gclass has as public | “Frame of an event that is not public, refused”, with event capped to 64 bytes, said by iev_create_from_gbuffer(); up to 7.26.9 the kw was traced raw, the jwt with it |
A frame before the session is not even parsed when it is bigger than
max_pre_session_frame. An identity card is a few KB (its jwt the largest
part); parsed, a frame of [{},...] takes about 100 times its size (16 MB:
1.7 GB), so up to 7.25.21 the max block of a yuno (200 MB) let a peer nobody
had authenticated ask for some 20 GB. A gate whose peers send bigger cards
raises it:
{
"name": "input",
"gclass": "C_IEVENT_SRV",
"kw": {
"max_pre_session_frame": 1048576
}
}A card refused by the authentication is answered with a negative
EV_IDENTITY_CARD_ACK, logged by the authenticator, and the channel is
dropped at timeout_idgot. Up to 7.25.20 each refusal above was an error
with the whole kw (its jwt with it), followed by a second error, “event
UNKNOWN in not-session state”, with the whole kw again, credentials and
all; a jwt that was not a string went on to the authentication.
tests/c/c_ievent_srv_identity_card.