kiosk-server 0.5.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +549 -0
- data/LICENSE.txt +202 -0
- data/README.md +635 -0
- data/app/channels/kiosk_events.rb +138 -0
- data/app/controllers/kiosk/server/application_controller.rb +17 -0
- data/app/controllers/kiosk/server/assistants_controller.rb +96 -0
- data/app/controllers/kiosk/server/auth_controller.rb +187 -0
- data/app/controllers/kiosk/server/device_verify_controller.rb +101 -0
- data/app/controllers/kiosk/server/discovery_controller.rb +66 -0
- data/app/controllers/kiosk/server/jwks_controller.rb +15 -0
- data/app/controllers/kiosk/server/kyc_attestation_controller.rb +69 -0
- data/app/controllers/kiosk/server/kyc_callback_controller.rb +15 -0
- data/app/controllers/kiosk/server/oauth_device_authorization_controller.rb +73 -0
- data/app/controllers/kiosk/server/oauth_token_controller.rb +59 -0
- data/app/controllers/kiosk/server/open_api_controller.rb +20 -0
- data/app/controllers/kiosk/server/payment_setup_controller.rb +31 -0
- data/app/controllers/kiosk/server/verb_controller.rb +61 -0
- data/app/controllers/kiosk/server/wire_controller.rb +43 -0
- data/app/models/kiosk/cart_mandate.rb +21 -0
- data/app/models/kiosk/server/application_record.rb +10 -0
- data/app/models/kiosk/settlement.rb +12 -0
- data/app/models/kiosk/visit.rb +13 -0
- data/app/views/kiosk/server/assistants/show.html.erb +94 -0
- data/app/views/kiosk/server/device_verify/decided.html.erb +14 -0
- data/app/views/kiosk/server/device_verify/show.html.erb +53 -0
- data/lib/generators/kiosk/install/install_generator.rb +125 -0
- data/lib/generators/kiosk/install/templates/create_kiosk_auth_challenges.rb.tt +16 -0
- data/lib/generators/kiosk/install/templates/create_kiosk_device_authorizations.rb.tt +17 -0
- data/lib/generators/kiosk/install/templates/create_kiosk_events.rb.tt +22 -0
- data/lib/generators/kiosk/install/templates/create_kiosk_identity_tables.rb.tt +16 -0
- data/lib/generators/kiosk/install/templates/create_kiosk_kyc_attributes.rb.tt +20 -0
- data/lib/generators/kiosk/install/templates/create_kiosk_mandates.rb.tt +21 -0
- data/lib/generators/kiosk/install/templates/create_kiosk_pow_spent.rb.tt +16 -0
- data/lib/generators/kiosk/install/templates/create_kiosk_refunds.rb.tt +16 -0
- data/lib/generators/kiosk/install/templates/create_kiosk_revocations.rb.tt +16 -0
- data/lib/generators/kiosk/install/templates/create_kiosk_schema.rb.tt +17 -0
- data/lib/generators/kiosk/install/templates/create_kiosk_visits.rb.tt +16 -0
- data/lib/generators/kiosk/install/templates/initializer.rb.tt +53 -0
- data/lib/kiosk/handler.rb +43 -0
- data/lib/kiosk/owned.rb +17 -0
- data/lib/kiosk/payable.rb +90 -0
- data/lib/kiosk/server/account_binding.rb +188 -0
- data/lib/kiosk/server/account_holder_gate.rb +58 -0
- data/lib/kiosk/server/action_event.rb +60 -0
- data/lib/kiosk/server/actions.rb +14 -0
- data/lib/kiosk/server/agent_identity_providers/default_agent_idp.rb +89 -0
- data/lib/kiosk/server/agent_login.rb +49 -0
- data/lib/kiosk/server/agent_registration.rb +88 -0
- data/lib/kiosk/server/argument_decoder.rb +260 -0
- data/lib/kiosk/server/audit_sink.rb +42 -0
- data/lib/kiosk/server/auth_challenge.rb +33 -0
- data/lib/kiosk/server/auth_challenge_stores.rb +74 -0
- data/lib/kiosk/server/auth_toll.rb +44 -0
- data/lib/kiosk/server/binding_module_gate.rb +25 -0
- data/lib/kiosk/server/caller_timezone.rb +50 -0
- data/lib/kiosk/server/column_spending_cap.rb +26 -0
- data/lib/kiosk/server/configuration_extension.rb +328 -0
- data/lib/kiosk/server/controller_verb.rb +63 -0
- data/lib/kiosk/server/current_request.rb +31 -0
- data/lib/kiosk/server/cursor.rb +33 -0
- data/lib/kiosk/server/device_authorization.rb +136 -0
- data/lib/kiosk/server/device_authorization_stores.rb +184 -0
- data/lib/kiosk/server/device_code_grant.rb +145 -0
- data/lib/kiosk/server/device_verification.rb +46 -0
- data/lib/kiosk/server/engine.rb +203 -0
- data/lib/kiosk/server/errors.rb +339 -0
- data/lib/kiosk/server/event_stores.rb +124 -0
- data/lib/kiosk/server/events.rb +86 -0
- data/lib/kiosk/server/events_cable.rb +102 -0
- data/lib/kiosk/server/events_connection.rb +170 -0
- data/lib/kiosk/server/executor/repeat.rb +42 -0
- data/lib/kiosk/server/executor/spending_cap.rb +102 -0
- data/lib/kiosk/server/executor.rb +397 -0
- data/lib/kiosk/server/failure_log.rb +28 -0
- data/lib/kiosk/server/handler_mixin.rb +418 -0
- data/lib/kiosk/server/handler_registrations.rb +57 -0
- data/lib/kiosk/server/handler_serving.rb +197 -0
- data/lib/kiosk/server/headers.rb +82 -0
- data/lib/kiosk/server/headers_middleware.rb +31 -0
- data/lib/kiosk/server/identity_resolution.rb +20 -0
- data/lib/kiosk/server/issuer_middleware.rb +22 -0
- data/lib/kiosk/server/jwks.rb +14 -0
- data/lib/kiosk/server/jwt_issuer.rb +98 -0
- data/lib/kiosk/server/kyc.rb +261 -0
- data/lib/kiosk/server/kyc_verifier.rb +106 -0
- data/lib/kiosk/server/link_code.rb +90 -0
- data/lib/kiosk/server/linked_assistants.rb +116 -0
- data/lib/kiosk/server/mandate_verifier.rb +221 -0
- data/lib/kiosk/server/open_api.rb +543 -0
- data/lib/kiosk/server/page.rb +19 -0
- data/lib/kiosk/server/payment_claim.rb +293 -0
- data/lib/kiosk/server/payment_setup.rb +102 -0
- data/lib/kiosk/server/pop_verifier.rb +87 -0
- data/lib/kiosk/server/pow_gate.rb +121 -0
- data/lib/kiosk/server/pow_spent_stores.rb +96 -0
- data/lib/kiosk/server/pow_toll.rb +136 -0
- data/lib/kiosk/server/queries.rb +14 -0
- data/lib/kiosk/server/reputation_factors.rb +38 -0
- data/lib/kiosk/server/request_validation.rb +171 -0
- data/lib/kiosk/server/response_validation.rb +45 -0
- data/lib/kiosk/server/result.rb +25 -0
- data/lib/kiosk/server/revocation_stores.rb +57 -0
- data/lib/kiosk/server/routing.rb +76 -0
- data/lib/kiosk/server/schema_definitions.rb +351 -0
- data/lib/kiosk/server/schema_document.rb +116 -0
- data/lib/kiosk/server/schema_slots.rb +119 -0
- data/lib/kiosk/server/schemas/auth.schema.json +135 -0
- data/lib/kiosk/server/schemas/binding.schema.json +145 -0
- data/lib/kiosk/server/schemas/kyc.schema.json +114 -0
- data/lib/kiosk/server/schemas/mandates.schema.json +75 -0
- data/lib/kiosk/server/schemas/pow.schema.json +94 -0
- data/lib/kiosk/server/session_context.rb +88 -0
- data/lib/kiosk/server/settled_spend.rb +21 -0
- data/lib/kiosk/server/signing_key.rb +82 -0
- data/lib/kiosk/server/verb_registry.rb +77 -0
- data/lib/kiosk/server/verb_vocabulary.rb +163 -0
- data/lib/kiosk/server/verb_wire.rb +85 -0
- data/lib/kiosk/server/version.rb +7 -0
- data/lib/kiosk/server/well_known.rb +416 -0
- data/lib/kiosk/server/wire_rendering.rb +116 -0
- data/lib/kiosk/server/wrong_door.rb +42 -0
- data/lib/kiosk/server.rb +42 -0
- data/lib/kiosk/skill_link.rb +16 -0
- data/listen.py +278 -0
- data/requirements.txt +1 -0
- metadata +349 -0
data/listen.py
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Kiosk event-stream listener — the pinned reference client.
|
|
3
|
+
|
|
4
|
+
Opens ONE WebSocket to an operator's `events_url`, subscribes to the topics you
|
|
5
|
+
name, and writes every message it receives as one JSON line on stdout. The
|
|
6
|
+
operator pushes when something happens, so there is nothing to poll and no
|
|
7
|
+
cadence to invent.
|
|
8
|
+
|
|
9
|
+
python3 listen.py --url wss://shop.example/kiosk/events \
|
|
10
|
+
--token-file ~/.kiosk/https_shop.example_443/token \
|
|
11
|
+
--topic order_delivery:<order-id>
|
|
12
|
+
|
|
13
|
+
The bearer token is read from a file, never from the command line, where every
|
|
14
|
+
process listing and shell history would show it.
|
|
15
|
+
|
|
16
|
+
WAKE ON THE EVENT: run it as a background process your runtime TRACKS and wakes
|
|
17
|
+
you on when it exits. With `--until-event` it prints the first event and exits 0
|
|
18
|
+
the moment it arrives, or exits 5 at `--max-seconds` with nothing. On waking, act
|
|
19
|
+
on the event, tell your human, and start it again with `--since <its id>`. Keep
|
|
20
|
+
`--max-seconds` below the longest your runtime lets a background process live. A
|
|
21
|
+
process the runtime does not track -- nohup, `&`, setsid -- wakes nobody, and
|
|
22
|
+
neither does a line landing in a file.
|
|
23
|
+
|
|
24
|
+
python3 listen.py --url … --token-file … --topic kyc_verification \
|
|
25
|
+
--until-event --max-seconds 300
|
|
26
|
+
|
|
27
|
+
Each line is a JSON object with a `type`:
|
|
28
|
+
|
|
29
|
+
{"type":"open"} the socket is up and authenticated
|
|
30
|
+
{"type":"subscribed","topic":…,"head":…,"truncated":…}
|
|
31
|
+
{"type":"event","id":…,"topic":…,"subject":…,"occurred_at":…,"data":{…}}
|
|
32
|
+
{"type":"rejected","topic":…} that topic is not yours to read
|
|
33
|
+
{"type":"unsubscribed","topic":…,"reason":…} you lost reach mid-stream
|
|
34
|
+
{"type":"disconnect","reason":…,"reconnect":…}
|
|
35
|
+
{"type":"reconnecting","in":…,"since":…}
|
|
36
|
+
{"type":"error","message":…}
|
|
37
|
+
|
|
38
|
+
EXIT CODES: 0 you asked it to stop (--max-seconds), or --until-event got its
|
|
39
|
+
event; 2 the arguments or the URL are wrong; 3 the operator will not have this
|
|
40
|
+
token back and no --token-command was given — mint a fresh one and start it
|
|
41
|
+
again; 4 nothing you asked for can arrive: every topic you named was refused,
|
|
42
|
+
or the origin serves no event stream (`501 module_not_served`); 5
|
|
43
|
+
--until-event reached the deadline with nothing.
|
|
44
|
+
|
|
45
|
+
It needs `websockets` from PyPI (>= 12, the release that added the threading
|
|
46
|
+
client). Add it to the same venv the skill already has you create:
|
|
47
|
+
|
|
48
|
+
pip install 'websockets>=12'
|
|
49
|
+
|
|
50
|
+
Do NOT replace this file with a client of your own. The subprotocol token, the
|
|
51
|
+
string-inside-a-string `identifier`, and the resume contract are each a place a
|
|
52
|
+
hand-rolled client is dropped with no diagnostic, and you would read that
|
|
53
|
+
silence as "nothing happened".
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
import argparse
|
|
57
|
+
import json
|
|
58
|
+
import os
|
|
59
|
+
import random
|
|
60
|
+
import subprocess
|
|
61
|
+
import sys
|
|
62
|
+
import time
|
|
63
|
+
from urllib.parse import urlencode, urlsplit, urlunsplit
|
|
64
|
+
|
|
65
|
+
import websockets
|
|
66
|
+
from websockets.sync.client import connect
|
|
67
|
+
|
|
68
|
+
SUBPROTOCOL = "actioncable-v1-json"
|
|
69
|
+
# The operator's own beat is one ping every thirty seconds. Three missed in a
|
|
70
|
+
# row is a socket that is gone without having said so — half-open TCP, a dozing
|
|
71
|
+
# laptop, an edge that dropped us — and the only way to find out is to stop
|
|
72
|
+
# waiting.
|
|
73
|
+
IDLE_TIMEOUT = 95
|
|
74
|
+
BACKOFF_CAP = 30
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def emit(**line):
|
|
78
|
+
"""One JSON object per line, flushed, so a tail sees it immediately."""
|
|
79
|
+
sys.stdout.write(json.dumps(line, separators=(",", ":")) + "\n")
|
|
80
|
+
sys.stdout.flush()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def problem_code(response):
|
|
84
|
+
"""The `code` of a refused upgrade's problem document, when it carries one."""
|
|
85
|
+
try:
|
|
86
|
+
return json.loads(response.body)["code"]
|
|
87
|
+
except (TypeError, ValueError, KeyError):
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def stream_url(url, topics, since):
|
|
92
|
+
"""`?topic=` is how you subscribe: the operator reads it on connect and
|
|
93
|
+
confirms each one, so a round trip per topic is not needed."""
|
|
94
|
+
query = [("topic", ",".join(topics))]
|
|
95
|
+
if since is not None:
|
|
96
|
+
query.append(("since", str(since)))
|
|
97
|
+
parts = urlsplit(url)
|
|
98
|
+
return urlunsplit((parts.scheme, parts.netloc, parts.path, urlencode(query), ""))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def read_frames(socket, state, deadline):
|
|
102
|
+
"""Drain one connection, and say what to do next.
|
|
103
|
+
|
|
104
|
+
"retry" reconnect · "stop" --max-seconds is up · "event" --until-event got
|
|
105
|
+
its event · "credential" the operator will not have this token back ·
|
|
106
|
+
"unreadable" every topic was refused.
|
|
107
|
+
|
|
108
|
+
The read is bounded rather than blocking forever: a deadline nothing
|
|
109
|
+
checks is not a deadline, and a socket that died without saying so looks
|
|
110
|
+
exactly like a quiet one.
|
|
111
|
+
"""
|
|
112
|
+
while True:
|
|
113
|
+
budget = IDLE_TIMEOUT
|
|
114
|
+
if deadline:
|
|
115
|
+
budget = min(budget, deadline - time.monotonic())
|
|
116
|
+
if budget <= 0:
|
|
117
|
+
return "stop"
|
|
118
|
+
try:
|
|
119
|
+
raw = socket.recv(timeout=budget)
|
|
120
|
+
except TimeoutError:
|
|
121
|
+
# The deadline, or three of the operator's beats missed — a socket
|
|
122
|
+
# that died without saying so. Neither is an error worth a line.
|
|
123
|
+
return "stop" if deadline and time.monotonic() >= deadline else "retry"
|
|
124
|
+
frame = json.loads(raw)
|
|
125
|
+
kind = frame.get("type")
|
|
126
|
+
|
|
127
|
+
if kind in ("ping", "welcome"):
|
|
128
|
+
continue
|
|
129
|
+
|
|
130
|
+
if kind == "confirm_subscription":
|
|
131
|
+
state["confirmed"] += 1
|
|
132
|
+
continue
|
|
133
|
+
|
|
134
|
+
if kind == "reject_subscription":
|
|
135
|
+
emit(type="rejected", topic=topic_of(frame))
|
|
136
|
+
state["rejected"] += 1
|
|
137
|
+
# EVERY topic refused and none confirmed: there is nothing on
|
|
138
|
+
# this socket to wait for, ever. Say so rather than sit on it.
|
|
139
|
+
if state["confirmed"] == 0 and state["rejected"] >= state["wanted"]:
|
|
140
|
+
return "unreadable"
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
if kind == "disconnect":
|
|
144
|
+
emit(type="disconnect", reason=frame.get("reason"),
|
|
145
|
+
reconnect=frame.get("reconnect", True))
|
|
146
|
+
# `reconnect: false` is the operator saying this credential is done:
|
|
147
|
+
# it expired, or the human revoked the assistant. A retry with the
|
|
148
|
+
# same bearer loses the same argument, so mint a new one or stop.
|
|
149
|
+
if frame.get("reconnect", True):
|
|
150
|
+
return "retry"
|
|
151
|
+
return "retry" if state["refresh"] else "credential"
|
|
152
|
+
|
|
153
|
+
message = frame.get("message")
|
|
154
|
+
if not isinstance(message, dict):
|
|
155
|
+
continue
|
|
156
|
+
|
|
157
|
+
if message.get("type") == "subscribed":
|
|
158
|
+
# `head` is where the operator's tail stood when this subscription
|
|
159
|
+
# opened. Adopt it as the resume floor NOW rather than waiting for
|
|
160
|
+
# a first event: a socket that drops before anything arrives would
|
|
161
|
+
# otherwise reconnect asking for nothing in particular, and every
|
|
162
|
+
# event emitted during the gap would be missed in silence.
|
|
163
|
+
if message.get("head") is not None:
|
|
164
|
+
state["since"] = max(state["since"] or 0, int(message["head"]))
|
|
165
|
+
emit(**message)
|
|
166
|
+
continue
|
|
167
|
+
|
|
168
|
+
if message.get("type") == "unsubscribed":
|
|
169
|
+
emit(**message)
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
# An event. Its `id` is per-operator and monotonic, so the highest one
|
|
173
|
+
# seen is exactly what `since` wants after a drop.
|
|
174
|
+
if message.get("id") is not None:
|
|
175
|
+
state["since"] = max(state["since"] or 0, int(message["id"]))
|
|
176
|
+
emit(type="event", **message)
|
|
177
|
+
# The one-shot wait is over the moment the thing you waited for lands.
|
|
178
|
+
# A `subscribed` confirmation is not that, and neither is a beat.
|
|
179
|
+
if state["one_shot"]:
|
|
180
|
+
return "event"
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def topic_of(frame):
|
|
184
|
+
try:
|
|
185
|
+
return json.loads(frame.get("identifier") or "{}").get("topic")
|
|
186
|
+
except ValueError:
|
|
187
|
+
return None
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def main():
|
|
191
|
+
parser = argparse.ArgumentParser(description="Kiosk event-stream listener")
|
|
192
|
+
parser.add_argument("--url", required=True, help="the operator's events_url")
|
|
193
|
+
parser.add_argument("--token-file", required=True, metavar="PATH",
|
|
194
|
+
help="a file holding your bearer token")
|
|
195
|
+
parser.add_argument("--topic", action="append", required=True, metavar="NAME[:SUBJECT]",
|
|
196
|
+
help="repeatable; NAME:SUBJECT narrows to one row")
|
|
197
|
+
parser.add_argument("--since", type=int, help="resume after this event id")
|
|
198
|
+
parser.add_argument("--token-command",
|
|
199
|
+
help="shell command printing a fresh bearer token; run when the "
|
|
200
|
+
"operator says this one will not be accepted back")
|
|
201
|
+
parser.add_argument("--max-seconds", type=float, help="stop after this long")
|
|
202
|
+
parser.add_argument("--until-event", action="store_true",
|
|
203
|
+
help="exit 0 on the first event, 5 at --max-seconds with none")
|
|
204
|
+
args = parser.parse_args()
|
|
205
|
+
|
|
206
|
+
if urlsplit(args.url).scheme not in ("ws", "wss"):
|
|
207
|
+
parser.error("--url must be ws:// or wss://")
|
|
208
|
+
if args.until_event and not args.max_seconds:
|
|
209
|
+
parser.error("--until-event needs --max-seconds: a wait with no deadline never returns")
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
with open(os.path.expanduser(args.token_file)) as file:
|
|
213
|
+
token = file.read().strip()
|
|
214
|
+
except OSError as error:
|
|
215
|
+
parser.error(f"--token-file: {error.strerror}")
|
|
216
|
+
|
|
217
|
+
state = {"since": args.since, "token": token, "refresh": args.token_command,
|
|
218
|
+
"wanted": len(args.topic), "confirmed": 0, "rejected": 0,
|
|
219
|
+
"one_shot": args.until_event}
|
|
220
|
+
deadline = time.monotonic() + args.max_seconds if args.max_seconds else None
|
|
221
|
+
attempt = 0
|
|
222
|
+
|
|
223
|
+
while True:
|
|
224
|
+
if deadline and time.monotonic() >= deadline:
|
|
225
|
+
return 5 if args.until_event else 0
|
|
226
|
+
renew = True
|
|
227
|
+
try:
|
|
228
|
+
with connect(stream_url(args.url, args.topic, state["since"]),
|
|
229
|
+
subprotocols=[SUBPROTOCOL],
|
|
230
|
+
additional_headers={"Authorization": f"Bearer {state['token']}"},
|
|
231
|
+
open_timeout=15, close_timeout=5) as socket:
|
|
232
|
+
emit(type="open")
|
|
233
|
+
attempt = 0
|
|
234
|
+
state["confirmed"] = state["rejected"] = 0
|
|
235
|
+
verdict = read_frames(socket, state, deadline)
|
|
236
|
+
if verdict == "event":
|
|
237
|
+
return 0
|
|
238
|
+
if verdict == "stop":
|
|
239
|
+
return 5 if args.until_event else 0
|
|
240
|
+
if verdict == "unreadable":
|
|
241
|
+
return 4
|
|
242
|
+
if verdict == "credential":
|
|
243
|
+
return 3
|
|
244
|
+
except KeyboardInterrupt:
|
|
245
|
+
return 0
|
|
246
|
+
except websockets.InvalidStatus as error:
|
|
247
|
+
status = error.response.status_code
|
|
248
|
+
code = problem_code(error.response)
|
|
249
|
+
emit(type="error", message=f"upgrade refused: {status}" + (f" {code}" if code else ""))
|
|
250
|
+
# 501: this origin serves no event stream, for any credential.
|
|
251
|
+
if status == 501:
|
|
252
|
+
return 4
|
|
253
|
+
if status not in (401, 403):
|
|
254
|
+
renew = False
|
|
255
|
+
elif state["refresh"] is None:
|
|
256
|
+
return 3
|
|
257
|
+
except Exception as error: # noqa: BLE001 — every transport fault is one retry
|
|
258
|
+
emit(type="error", message=f"{type(error).__name__}: {error}")
|
|
259
|
+
|
|
260
|
+
if renew and state["refresh"] is not None:
|
|
261
|
+
fresh = subprocess.run(state["refresh"], shell=True, capture_output=True,
|
|
262
|
+
text=True, check=False)
|
|
263
|
+
if fresh.returncode == 0 and fresh.stdout.strip():
|
|
264
|
+
state["token"] = fresh.stdout.strip()
|
|
265
|
+
else:
|
|
266
|
+
emit(type="error", message="--token-command produced no token")
|
|
267
|
+
return 3
|
|
268
|
+
|
|
269
|
+
attempt += 1
|
|
270
|
+
wait = min(2 ** (attempt - 1), BACKOFF_CAP) * (0.5 + random.random())
|
|
271
|
+
if deadline:
|
|
272
|
+
wait = min(wait, max(0.0, deadline - time.monotonic()))
|
|
273
|
+
emit(type="reconnecting", **{"in": round(wait, 2), "since": state["since"]})
|
|
274
|
+
time.sleep(wait)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
if __name__ == "__main__":
|
|
278
|
+
sys.exit(main())
|
data/requirements.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
websockets>=12
|
metadata
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: kiosk-server
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.5.20
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Phil Pirozhkov
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: kiosk-core
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.5.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.5.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: jwt
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.8'
|
|
33
|
+
- - "<"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '4.0'
|
|
36
|
+
type: :runtime
|
|
37
|
+
prerelease: false
|
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '2.8'
|
|
43
|
+
- - "<"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '4.0'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: base64
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '0'
|
|
53
|
+
type: :runtime
|
|
54
|
+
prerelease: false
|
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '0'
|
|
60
|
+
- !ruby/object:Gem::Dependency
|
|
61
|
+
name: zeitwerk
|
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - "~>"
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '2.6'
|
|
67
|
+
type: :runtime
|
|
68
|
+
prerelease: false
|
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - "~>"
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '2.6'
|
|
74
|
+
- !ruby/object:Gem::Dependency
|
|
75
|
+
name: json_schemer
|
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - ">="
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '2.3'
|
|
81
|
+
- - "<"
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '3.0'
|
|
84
|
+
type: :runtime
|
|
85
|
+
prerelease: false
|
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '2.3'
|
|
91
|
+
- - "<"
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: '3.0'
|
|
94
|
+
- !ruby/object:Gem::Dependency
|
|
95
|
+
name: railties
|
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
|
97
|
+
requirements:
|
|
98
|
+
- - "~>"
|
|
99
|
+
- !ruby/object:Gem::Version
|
|
100
|
+
version: '8.1'
|
|
101
|
+
type: :runtime
|
|
102
|
+
prerelease: false
|
|
103
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
104
|
+
requirements:
|
|
105
|
+
- - "~>"
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
version: '8.1'
|
|
108
|
+
- !ruby/object:Gem::Dependency
|
|
109
|
+
name: actionpack
|
|
110
|
+
requirement: !ruby/object:Gem::Requirement
|
|
111
|
+
requirements:
|
|
112
|
+
- - "~>"
|
|
113
|
+
- !ruby/object:Gem::Version
|
|
114
|
+
version: '8.1'
|
|
115
|
+
type: :runtime
|
|
116
|
+
prerelease: false
|
|
117
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
118
|
+
requirements:
|
|
119
|
+
- - "~>"
|
|
120
|
+
- !ruby/object:Gem::Version
|
|
121
|
+
version: '8.1'
|
|
122
|
+
- !ruby/object:Gem::Dependency
|
|
123
|
+
name: actioncable
|
|
124
|
+
requirement: !ruby/object:Gem::Requirement
|
|
125
|
+
requirements:
|
|
126
|
+
- - "~>"
|
|
127
|
+
- !ruby/object:Gem::Version
|
|
128
|
+
version: '8.1'
|
|
129
|
+
type: :runtime
|
|
130
|
+
prerelease: false
|
|
131
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
132
|
+
requirements:
|
|
133
|
+
- - "~>"
|
|
134
|
+
- !ruby/object:Gem::Version
|
|
135
|
+
version: '8.1'
|
|
136
|
+
- !ruby/object:Gem::Dependency
|
|
137
|
+
name: activerecord
|
|
138
|
+
requirement: !ruby/object:Gem::Requirement
|
|
139
|
+
requirements:
|
|
140
|
+
- - "~>"
|
|
141
|
+
- !ruby/object:Gem::Version
|
|
142
|
+
version: '8.1'
|
|
143
|
+
type: :runtime
|
|
144
|
+
prerelease: false
|
|
145
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
146
|
+
requirements:
|
|
147
|
+
- - "~>"
|
|
148
|
+
- !ruby/object:Gem::Version
|
|
149
|
+
version: '8.1'
|
|
150
|
+
- !ruby/object:Gem::Dependency
|
|
151
|
+
name: activesupport
|
|
152
|
+
requirement: !ruby/object:Gem::Requirement
|
|
153
|
+
requirements:
|
|
154
|
+
- - "~>"
|
|
155
|
+
- !ruby/object:Gem::Version
|
|
156
|
+
version: '8.1'
|
|
157
|
+
type: :runtime
|
|
158
|
+
prerelease: false
|
|
159
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
160
|
+
requirements:
|
|
161
|
+
- - "~>"
|
|
162
|
+
- !ruby/object:Gem::Version
|
|
163
|
+
version: '8.1'
|
|
164
|
+
description: |
|
|
165
|
+
kiosk-server is the host-side surface for Kiosk. The full surface ships:
|
|
166
|
+
|
|
167
|
+
- The controllers — the per-verb wire (GET <endpoint>/<query-name>,
|
|
168
|
+
POST <endpoint>/<action-name>) and the reserved endpoints
|
|
169
|
+
(/kiosk/schema, /kiosk/pay), a derived OpenAPI description of both,
|
|
170
|
+
the register/login proof-of-possession auth plane, JWKS, the KYC
|
|
171
|
+
attestation endpoint, agents.txt / agents.json / kiosk.json
|
|
172
|
+
discovery, and the account-binding ceremony (RFC 8628-shaped device
|
|
173
|
+
authorization, the possession-proof-gated token poll, the verify and
|
|
174
|
+
«Link an assistant» pages).
|
|
175
|
+
- Kiosk::Server::Executor — dispatches a resolved command to the
|
|
176
|
+
queries and actions the host registered.
|
|
177
|
+
- Agent registration and login, with a pluggable agent-IdP.
|
|
178
|
+
- The PoW gate that enforces a kiosk-reputation policy's challenge
|
|
179
|
+
(soft dependency; zero overhead when no policy is set).
|
|
180
|
+
- A Rack middleware injecting the Kiosk-Server-Version /
|
|
181
|
+
Kiosk-API-Version / Kiosk-Min-Client response headers, a pure-Ruby
|
|
182
|
+
builder for the discovery documents, SQL generators for the
|
|
183
|
+
canonical schema migrations, and a `kiosk:install` generator that
|
|
184
|
+
lays down the initializer and the migrations.
|
|
185
|
+
|
|
186
|
+
The Rails engine serves that whole surface from one `kiosk` block in
|
|
187
|
+
config/routes.rb, which mounts it at Kiosk.configuration.mount_path —
|
|
188
|
+
drawing the wire/auth/JWKS/KYC/binding routes under the mount and
|
|
189
|
+
installing the root-relative discovery routes (agents.txt, .well-known)
|
|
190
|
+
into the host — then one route per query and action the block names. It
|
|
191
|
+
also auto-injects the headers middleware.
|
|
192
|
+
email:
|
|
193
|
+
- hello@fili.pp.ru
|
|
194
|
+
executables: []
|
|
195
|
+
extensions: []
|
|
196
|
+
extra_rdoc_files: []
|
|
197
|
+
files:
|
|
198
|
+
- CHANGELOG.md
|
|
199
|
+
- LICENSE.txt
|
|
200
|
+
- README.md
|
|
201
|
+
- app/channels/kiosk_events.rb
|
|
202
|
+
- app/controllers/kiosk/server/application_controller.rb
|
|
203
|
+
- app/controllers/kiosk/server/assistants_controller.rb
|
|
204
|
+
- app/controllers/kiosk/server/auth_controller.rb
|
|
205
|
+
- app/controllers/kiosk/server/device_verify_controller.rb
|
|
206
|
+
- app/controllers/kiosk/server/discovery_controller.rb
|
|
207
|
+
- app/controllers/kiosk/server/jwks_controller.rb
|
|
208
|
+
- app/controllers/kiosk/server/kyc_attestation_controller.rb
|
|
209
|
+
- app/controllers/kiosk/server/kyc_callback_controller.rb
|
|
210
|
+
- app/controllers/kiosk/server/oauth_device_authorization_controller.rb
|
|
211
|
+
- app/controllers/kiosk/server/oauth_token_controller.rb
|
|
212
|
+
- app/controllers/kiosk/server/open_api_controller.rb
|
|
213
|
+
- app/controllers/kiosk/server/payment_setup_controller.rb
|
|
214
|
+
- app/controllers/kiosk/server/verb_controller.rb
|
|
215
|
+
- app/controllers/kiosk/server/wire_controller.rb
|
|
216
|
+
- app/models/kiosk/cart_mandate.rb
|
|
217
|
+
- app/models/kiosk/server/application_record.rb
|
|
218
|
+
- app/models/kiosk/settlement.rb
|
|
219
|
+
- app/models/kiosk/visit.rb
|
|
220
|
+
- app/views/kiosk/server/assistants/show.html.erb
|
|
221
|
+
- app/views/kiosk/server/device_verify/decided.html.erb
|
|
222
|
+
- app/views/kiosk/server/device_verify/show.html.erb
|
|
223
|
+
- lib/generators/kiosk/install/install_generator.rb
|
|
224
|
+
- lib/generators/kiosk/install/templates/create_kiosk_auth_challenges.rb.tt
|
|
225
|
+
- lib/generators/kiosk/install/templates/create_kiosk_device_authorizations.rb.tt
|
|
226
|
+
- lib/generators/kiosk/install/templates/create_kiosk_events.rb.tt
|
|
227
|
+
- lib/generators/kiosk/install/templates/create_kiosk_identity_tables.rb.tt
|
|
228
|
+
- lib/generators/kiosk/install/templates/create_kiosk_kyc_attributes.rb.tt
|
|
229
|
+
- lib/generators/kiosk/install/templates/create_kiosk_mandates.rb.tt
|
|
230
|
+
- lib/generators/kiosk/install/templates/create_kiosk_pow_spent.rb.tt
|
|
231
|
+
- lib/generators/kiosk/install/templates/create_kiosk_refunds.rb.tt
|
|
232
|
+
- lib/generators/kiosk/install/templates/create_kiosk_revocations.rb.tt
|
|
233
|
+
- lib/generators/kiosk/install/templates/create_kiosk_schema.rb.tt
|
|
234
|
+
- lib/generators/kiosk/install/templates/create_kiosk_visits.rb.tt
|
|
235
|
+
- lib/generators/kiosk/install/templates/initializer.rb.tt
|
|
236
|
+
- lib/kiosk/handler.rb
|
|
237
|
+
- lib/kiosk/owned.rb
|
|
238
|
+
- lib/kiosk/payable.rb
|
|
239
|
+
- lib/kiosk/server.rb
|
|
240
|
+
- lib/kiosk/server/account_binding.rb
|
|
241
|
+
- lib/kiosk/server/account_holder_gate.rb
|
|
242
|
+
- lib/kiosk/server/action_event.rb
|
|
243
|
+
- lib/kiosk/server/actions.rb
|
|
244
|
+
- lib/kiosk/server/agent_identity_providers/default_agent_idp.rb
|
|
245
|
+
- lib/kiosk/server/agent_login.rb
|
|
246
|
+
- lib/kiosk/server/agent_registration.rb
|
|
247
|
+
- lib/kiosk/server/argument_decoder.rb
|
|
248
|
+
- lib/kiosk/server/audit_sink.rb
|
|
249
|
+
- lib/kiosk/server/auth_challenge.rb
|
|
250
|
+
- lib/kiosk/server/auth_challenge_stores.rb
|
|
251
|
+
- lib/kiosk/server/auth_toll.rb
|
|
252
|
+
- lib/kiosk/server/binding_module_gate.rb
|
|
253
|
+
- lib/kiosk/server/caller_timezone.rb
|
|
254
|
+
- lib/kiosk/server/column_spending_cap.rb
|
|
255
|
+
- lib/kiosk/server/configuration_extension.rb
|
|
256
|
+
- lib/kiosk/server/controller_verb.rb
|
|
257
|
+
- lib/kiosk/server/current_request.rb
|
|
258
|
+
- lib/kiosk/server/cursor.rb
|
|
259
|
+
- lib/kiosk/server/device_authorization.rb
|
|
260
|
+
- lib/kiosk/server/device_authorization_stores.rb
|
|
261
|
+
- lib/kiosk/server/device_code_grant.rb
|
|
262
|
+
- lib/kiosk/server/device_verification.rb
|
|
263
|
+
- lib/kiosk/server/engine.rb
|
|
264
|
+
- lib/kiosk/server/errors.rb
|
|
265
|
+
- lib/kiosk/server/event_stores.rb
|
|
266
|
+
- lib/kiosk/server/events.rb
|
|
267
|
+
- lib/kiosk/server/events_cable.rb
|
|
268
|
+
- lib/kiosk/server/events_connection.rb
|
|
269
|
+
- lib/kiosk/server/executor.rb
|
|
270
|
+
- lib/kiosk/server/executor/repeat.rb
|
|
271
|
+
- lib/kiosk/server/executor/spending_cap.rb
|
|
272
|
+
- lib/kiosk/server/failure_log.rb
|
|
273
|
+
- lib/kiosk/server/handler_mixin.rb
|
|
274
|
+
- lib/kiosk/server/handler_registrations.rb
|
|
275
|
+
- lib/kiosk/server/handler_serving.rb
|
|
276
|
+
- lib/kiosk/server/headers.rb
|
|
277
|
+
- lib/kiosk/server/headers_middleware.rb
|
|
278
|
+
- lib/kiosk/server/identity_resolution.rb
|
|
279
|
+
- lib/kiosk/server/issuer_middleware.rb
|
|
280
|
+
- lib/kiosk/server/jwks.rb
|
|
281
|
+
- lib/kiosk/server/jwt_issuer.rb
|
|
282
|
+
- lib/kiosk/server/kyc.rb
|
|
283
|
+
- lib/kiosk/server/kyc_verifier.rb
|
|
284
|
+
- lib/kiosk/server/link_code.rb
|
|
285
|
+
- lib/kiosk/server/linked_assistants.rb
|
|
286
|
+
- lib/kiosk/server/mandate_verifier.rb
|
|
287
|
+
- lib/kiosk/server/open_api.rb
|
|
288
|
+
- lib/kiosk/server/page.rb
|
|
289
|
+
- lib/kiosk/server/payment_claim.rb
|
|
290
|
+
- lib/kiosk/server/payment_setup.rb
|
|
291
|
+
- lib/kiosk/server/pop_verifier.rb
|
|
292
|
+
- lib/kiosk/server/pow_gate.rb
|
|
293
|
+
- lib/kiosk/server/pow_spent_stores.rb
|
|
294
|
+
- lib/kiosk/server/pow_toll.rb
|
|
295
|
+
- lib/kiosk/server/queries.rb
|
|
296
|
+
- lib/kiosk/server/reputation_factors.rb
|
|
297
|
+
- lib/kiosk/server/request_validation.rb
|
|
298
|
+
- lib/kiosk/server/response_validation.rb
|
|
299
|
+
- lib/kiosk/server/result.rb
|
|
300
|
+
- lib/kiosk/server/revocation_stores.rb
|
|
301
|
+
- lib/kiosk/server/routing.rb
|
|
302
|
+
- lib/kiosk/server/schema_definitions.rb
|
|
303
|
+
- lib/kiosk/server/schema_document.rb
|
|
304
|
+
- lib/kiosk/server/schema_slots.rb
|
|
305
|
+
- lib/kiosk/server/schemas/auth.schema.json
|
|
306
|
+
- lib/kiosk/server/schemas/binding.schema.json
|
|
307
|
+
- lib/kiosk/server/schemas/kyc.schema.json
|
|
308
|
+
- lib/kiosk/server/schemas/mandates.schema.json
|
|
309
|
+
- lib/kiosk/server/schemas/pow.schema.json
|
|
310
|
+
- lib/kiosk/server/session_context.rb
|
|
311
|
+
- lib/kiosk/server/settled_spend.rb
|
|
312
|
+
- lib/kiosk/server/signing_key.rb
|
|
313
|
+
- lib/kiosk/server/verb_registry.rb
|
|
314
|
+
- lib/kiosk/server/verb_vocabulary.rb
|
|
315
|
+
- lib/kiosk/server/verb_wire.rb
|
|
316
|
+
- lib/kiosk/server/version.rb
|
|
317
|
+
- lib/kiosk/server/well_known.rb
|
|
318
|
+
- lib/kiosk/server/wire_rendering.rb
|
|
319
|
+
- lib/kiosk/server/wrong_door.rb
|
|
320
|
+
- lib/kiosk/skill_link.rb
|
|
321
|
+
- listen.py
|
|
322
|
+
- requirements.txt
|
|
323
|
+
homepage: https://kiosk.tech
|
|
324
|
+
licenses:
|
|
325
|
+
- Apache-2.0
|
|
326
|
+
metadata:
|
|
327
|
+
homepage_uri: https://kiosk.tech
|
|
328
|
+
source_code_uri: https://github.com/kiosk-hq/kiosk
|
|
329
|
+
changelog_uri: https://github.com/kiosk-hq/kiosk/blob/main/kiosk-server/CHANGELOG.md
|
|
330
|
+
bug_tracker_uri: https://github.com/kiosk-hq/kiosk/issues
|
|
331
|
+
rubygems_mfa_required: 'true'
|
|
332
|
+
rdoc_options: []
|
|
333
|
+
require_paths:
|
|
334
|
+
- lib
|
|
335
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
336
|
+
requirements:
|
|
337
|
+
- - ">="
|
|
338
|
+
- !ruby/object:Gem::Version
|
|
339
|
+
version: '4.0'
|
|
340
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
341
|
+
requirements:
|
|
342
|
+
- - ">="
|
|
343
|
+
- !ruby/object:Gem::Version
|
|
344
|
+
version: '0'
|
|
345
|
+
requirements: []
|
|
346
|
+
rubygems_version: 4.0.3
|
|
347
|
+
specification_version: 4
|
|
348
|
+
summary: Rails engine + Rack middleware + pure-Ruby helpers for the Kiosk framework
|
|
349
|
+
test_files: []
|