viafrei 1.3.12 → 1.3.16

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.
package/LICENSE CHANGED
@@ -210,7 +210,8 @@ bridge in this repository.
210
210
 
211
211
  Data obtained through the ViaFrei MCP server keeps its own provider licence and
212
212
  is not relicensed by anything here. Several of those licences are share-alike
213
- (DELFI public-transport data is CC BY-SA 4.0) or purpose-limited (MTS-K fuel
213
+ (DELFI public-transport data is Creative Commons Attribution-ShareAlike; its
214
+ catalogue entry states no version — see SOURCES.md) or purpose-limited (MTS-K fuel
214
215
  prices are consumer information only, with no redistribution in any form,
215
216
  aggregates included).
216
217
 
package/NOTICE CHANGED
@@ -10,15 +10,31 @@ keeps its provider's own terms. Those terms travel with the answer, not with
10
10
  this package, and several of them are share-alike or purpose-limited. In
11
11
  particular:
12
12
 
13
- - DELFI public-transport data is CC BY-SA 4.0. Share-alike: anything derived
14
- from it inherits the obligation, and it must not be blended into a result
15
- carrying a different licence.
13
+ - DELFI public-transport data is Creative Commons Attribution-ShareAlike. The
14
+ catalogue record for the realtime feed names no version (see SOURCES.md), so
15
+ treat the share-alike as applying and do not rely on a version for a
16
+ derivative. Share-alike reaches ADAPTATIONS, not aggregations: if you
17
+ recompute, reshape or rearrange the data you must license that under BY-SA -
18
+ art. 1(a) of the 4.0 text names material "translated, altered, arranged,
19
+ transformed, or otherwise modified", so a rearrangement is inside the
20
+ obligation and not outside it - but showing it beside another source's data
21
+ is an aggregation and puts no obligation on the other source. Which version
22
+ applies is the open question SOURCES.md sets out; the words above are quoted
23
+ from 4.0 because that is the text we read.
16
24
  - MTS-K fuel prices (Tankerkönig) are consumer information only. No
17
25
  redistribution in any form, aggregates and derived tables included.
18
- - Address results are derived from OpenStreetMap under the ODbL 1.0. The
19
- share-alike is on the database rather than on the sentence, and our offer
20
- under section 4.6 stands: ask and you get the extract and our alterations
21
- to it under the same licence.
26
+ - Address, point-of-interest and place results can be derived from
27
+ OpenStreetMap under the ODbL 1.0. What decides it is which TABLE answered,
28
+ not which tool you called: place resolution falls through our own gazetteer
29
+ to the OSM tables, so any answer about a place may be OSM-derived. Read
30
+ `_meta.sources` and the attribution line on the answer you actually got — an
31
+ OSM-derived result names `osm`. The share-alike is on the database (ODbL 4.4)
32
+ and not on the answer (ODbL 4.5(b)); the answer carries a notice instead
33
+ (ODbL 4.3). Our offer under ODbL 4.6 stands and covers BOTH extracts —
34
+ addresses and points of interest, one file each — plus our alterations to
35
+ them, under ODbL 1.0 and free of charge. Ask by opening an issue on this
36
+ repository, or by the e-mail address in the Impressum at
37
+ https://viafrei.de/impressum if you have no GitHub account.
22
38
  - Autobahn GmbH, BKG, DWD, Deutsche Bahn, GeoNames and the AFIR charging
23
39
  feeds each carry their own licence and attribution string.
24
40
 
package/README.md CHANGED
@@ -1,36 +1,51 @@
1
1
  # viafrei
2
2
 
3
- **German road, rail, parking, charging and fuel data — live, inside your AI
4
- assistant.**
3
+ [![npm](https://img.shields.io/npm/v/viafrei?color=cb3837&label=npm&logo=npm)](https://www.npmjs.com/package/viafrei)
4
+ [![node](https://img.shields.io/node/v/viafrei?logo=node.js&logoColor=white)](https://nodejs.org)
5
+ [![licence](https://img.shields.io/npm/l/viafrei?color=blue)](LICENSE)
6
+ [![no API key](https://img.shields.io/badge/API%20key-none-brightgreen)](#connect-in-one-line)
7
+ [![MCP](https://img.shields.io/badge/MCP-Streamable%20HTTP-6f42c1)](https://modelcontextprotocol.io)
8
+
9
+ **Live German traffic, rail, parking, charging, fuel, addresses and weather —
10
+ inside your AI assistant.** Ask in plain German or plain English and the answer
11
+ comes back from official open data, with the attribution the licence requires.
5
12
 
6
13
  ```
7
14
  npx viafrei
8
15
  ```
9
16
 
17
+ **No account, no API key, no sign-up.**
18
+
10
19
  ## Ask your assistant things like
11
20
 
12
21
  **Welche Züge fahren als Nächstes ab Hamburg Hbf?**
13
22
 
14
23
  ```
15
24
  Abfahrten ab Hamburg Hbf (nächste 60 Minuten):
16
- 09:45 ICE 519 → München Hbf, Gleis 14, +3 min (ca. 09:48)
17
- 09:51 IC 306 → Stockholm Central, Gleis 12, pünktlich
18
- 09:51 ICE 707 → Berlin Hbf, Gleis 8A-F, +1 min (ca. 09:52)
19
- … seven more
25
+ 12:28 RJ 384 → Koebenhavn H, Gleis 5, pünktlich
26
+ 12:29 ICE 7 → Karlsruhe Hbf, Gleis 14, pünktlich
27
+ 12:33 ICE 774 → Kiel Hbf, Gleis 11, pünktlich
28
+ 12:33 ME RB31 → Lüneburg, Gleis 13A-C, pünktlich
29
+ 12:34 ICE 601 → München Hbf, Gleis 8A-F, pünktlich
20
30
 
21
- Stand 09:43 · Quelle: Fahrplandaten: Deutsche Bahn AG, DB API Marketplace,
31
+ Stand 12:23 · Quelle: Fahrplandaten: Deutsche Bahn AG, DB API Marketplace,
22
32
  CC BY 4.0, bearbeitet (…) · Bahnhofsdaten: Deutsche Bahn AG,
23
33
  DB API Marketplace, CC BY 4.0, bearbeitet (…)
24
34
  ```
25
35
 
36
+ That is a real answer, not a mock-up: it came back from
37
+ `https://mcp.viafrei.de/mcp` on 2026-09-27 at 12:23 Berlin time. **Two edits, both
38
+ named:** the two source URIs are shortened to `…` for the reason in the next
39
+ paragraph, and only the first five of the ten departures it returned are shown.
40
+ The server sends no "… and five more" line — the truncation is this page's, not
41
+ its.
42
+
26
43
  Every answer ends with a line like that last one. It is the licence talking,
27
44
  and it is meant to be shown to whoever reads the answer — see
28
45
  [Using the data you get back](#using-the-data-you-get-back). **Reproduce the
29
46
  line the server sends you, not this one:** it has been shortened here, and the
30
47
  real one names each source's URL, which the licence requires you to keep.
31
48
 
32
- **No account, no API key, no sign-up — ask and the answer comes back.**
33
-
34
49
  ViaFrei is in its stabilisation and testing phase at the time of writing: live
35
50
  and free, with some sources thinner than they will be, and the occasional tool
36
51
  that answers slowly or not at all. **Tell us when that happens** —
@@ -48,27 +63,83 @@ the language you asked in, not translated from one house language:
48
63
  - *Wo kann ich in Leipzig mit Typ 2 laden?*
49
64
  - *Gibt es eine Unwetterwarnung für Freiburg?*
50
65
  - *Brauche ich in Deutschland eine Umweltplakette?*
66
+ - *Funktioniert der Aufzug am Bahnhof Köln Messe/Deutz?* — lift and escalator
67
+ status, which is the difference between a station being usable and not
68
+ - *Wo ist die nächste Apotheke zum Leipziger Hauptbahnhof?*
69
+ - *What is at 52.5163, 13.3777?* — and the other direction: a street address to
70
+ coordinates
51
71
  - *Tell me when the A8 reopens* — the server can watch a situation and tell
52
72
  your assistant when it changes, so you need not keep asking. The watch lives
53
73
  in the conversation that opened it: it reaches no inbox and no phone, and
54
74
  it ends with the session. Three hours by default; 24 is the longest one can
55
75
  be asked to run
56
76
 
57
- Thirteen tools at the time of writing, and no list of them on this page. A
58
- pasted catalogue goes stale the first time a description changes on the server,
59
- and this page is frozen inside a published tarball — it cannot be corrected
60
- without a release. So there is one source of truth and it is the running
61
- server: connect any MCP client and call `tools/list` for the catalogue as it
62
- is today. (<https://viafrei.de> is the live national traffic digest, not a
63
- catalogue.)
77
+ ## What it covers
64
78
 
65
- ## Quick start
79
+ **Eighteen tools** as of this release. Grouped by the question they answer,
80
+ because the grouping is stable and a pasted catalogue is not:
66
81
 
67
- Node 22 or newer. Nothing to install — `npx` fetches the bridge when your
68
- client starts it.
82
+ | | |
83
+ |---|---|
84
+ | **Roads** | live incidents, jams and closures; roadworks ahead on a route; the state of one Autobahn end to end; **lorry** parking on the motorways, with occupancy wherever the operator publishes it — see the note below on car parks |
85
+ | **Rail** | next departures from any station with real-time delays and platforms; how punctual public transport is across a region right now (region-wide — never one line, trip or stop); whether a station's lifts and escalators are working right now |
86
+ | **Energy** | charging points by connector type and power; the cheapest fuel near a place |
87
+ | **Places** | a street address to coordinates and back; points of interest; what is at a coordinate, and what is near it — nationwide, all sixteen Länder (measured on 2026-09-27 — [how](SOURCES.md)) |
88
+ | **Weather** | official DWD severe-weather warnings for a place |
89
+ | **Watches** | ask once and be told when a situation changes, instead of asking again |
90
+
91
+ **One thing the parking row does not yet cover: car parks.** The licence for
92
+ station car parks is read and the loader exists, but nothing on the public service
93
+ answers with one today — asked for a `car_park` in Köln, Hamburg and Leipzig on
94
+ 2026-09-27, every facility that came back was a lorry park or an unclassified site,
95
+ and none carried occupancy. So what you get from `find_parking` today is motorway
96
+ lorry parking. The table said "car and lorry parking" until this release; it was
97
+ the same over-claim the Rail row carried about disruptions, and it is corrected here rather
98
+ than left for a user to discover.
99
+
100
+ **No tool names, descriptions or schemas are written by hand on this page, on
101
+ purpose.** This file is frozen inside a published tarball and cannot be corrected
102
+ without a release, so a hand-copied catalogue would start rotting the first time a
103
+ description changed on the server.
104
+
105
+ **[API.md](API.md) is the way round that, and it is honest about what it is.** It
106
+ is *generated* from [`catalogue.json`](catalogue.json) — a snapshot of what the
107
+ production server answered when asked to describe itself — and CI fails if the two
108
+ have drifted, so the document cannot quietly disagree with the snapshot. What that
109
+ does not fix is the snapshot ageing relative to the live server: a capture is a
110
+ point in time, and API.md's header carries the date it was taken. **The source of
111
+ truth is still the running server**: connect any MCP client and call `tools/list`.
112
+ (<https://viafrei.de> is the live national traffic digest, not a catalogue.)
113
+
114
+ ## Connect in one line
115
+
116
+ The product is a **hosted MCP server**. There is nothing to deploy, no key to
117
+ request and no quota to negotiate — point a client at it and the tools appear.
118
+
119
+ **Streamable HTTP — the address to use.** Most clients speak this, and they need
120
+ none of the rest of this page:
69
121
 
70
- **Claude Desktop** (`claude_desktop_config.json`). The same three lines fit any
71
- client that takes an stdio MCP server, including the `.mcp.json` an IDE reads:
122
+ ```
123
+ https://mcp.viafrei.de/mcp
124
+ ```
125
+
126
+ ```bash
127
+ claude mcp add --transport http viafrei https://mcp.viafrei.de/mcp
128
+ ```
129
+
130
+ **HTTP+SSE — if your client only speaks the older transport**, it is answered too,
131
+ so nobody meets a locked door. It is deprecated in the specification; prefer the
132
+ address above:
133
+
134
+ ```bash
135
+ claude mcp add --transport sse viafrei https://mcp.viafrei.de/sse
136
+ ```
137
+
138
+ **stdio — this package.** Some clients still speak only stdio, and **this bridge
139
+ is for those, and only those.** Node 22 or newer; nothing to install, because
140
+ `npx` fetches it when your client starts it. The same three lines fit any client
141
+ that takes an stdio MCP server, including the `.mcp.json` an IDE reads and
142
+ Claude Desktop's `claude_desktop_config.json`:
72
143
 
73
144
  ```json
74
145
  {
@@ -81,25 +152,54 @@ client that takes an stdio MCP server, including the `.mcp.json` an IDE reads:
81
152
  }
82
153
  ```
83
154
 
84
- Restart the client and ask it one of the questions above. There is no account,
85
- no API key and no sign-up.
155
+ Restart the client and ask it one of the questions above.
86
156
 
87
- ## Do you actually need this package?
157
+ ## Documentation
88
158
 
89
- Probably not — and that is deliberate.
90
-
91
- Most MCP clients speak Streamable HTTP and should connect straight to
92
- `https://mcp.viafrei.de/mcp`. **They do not need this package at all.**
93
-
94
- Clients that speak only the older HTTP+SSE transport do not need it either: the
95
- server answers that transport too, at `https://mcp.viafrei.de/sse`
96
- (`claude mcp add --transport sse`). It is deprecated in the specification and
97
- carried so that nobody meets a locked door; prefer the address above.
98
-
99
- Some clients still speak only stdio. **This bridge is for those** — and only
100
- those: it runs locally, exposes a stdio MCP server, and relays every request to
101
- the public endpoint. It is a transport shim: it holds no data and no
102
- credentials, and it makes no decision about any answer.
159
+ | | |
160
+ |---|---|
161
+ | **[API.md](API.md)** | Every tool with its parameters, types, defaults and constraints, plus the resources, resource templates and prompts. Generated from a dated snapshot of the running server, so the descriptions are the server's own words — which is what your assistant actually reads when it picks a tool. |
162
+ | **[Wiki](https://github.com/mavrovde/viafrei-bridge/wiki)** | The prose half: [connecting your assistant](https://github.com/mavrovde/viafrei-bridge/wiki/Connecting-your-assistant), [tools at a glance](https://github.com/mavrovde/viafrei-bridge/wiki/Tools-at-a-glance), the [roadmap](https://github.com/mavrovde/viafrei-bridge/wiki/Roadmap) and an [FAQ](https://github.com/mavrovde/viafrei-bridge/wiki/FAQ). |
163
+ | **[SOURCES.md](SOURCES.md)** | Every publisher, what it covers, its licence, and the attribution line to reproduce — including the conditions that are licence breaches rather than style problems. |
164
+ | **[SUPPORT.md](SUPPORT.md)** | Where a question, a bad answer or a security report should go, and what makes a report easy to act on. |
165
+
166
+ ### Worked use cases
167
+
168
+ Five walkthroughs, each naming the tools that answer it and what comes back:
169
+
170
+ - [Driving Munich to Berlin](https://github.com/mavrovde/viafrei-bridge/wiki/Use-case-Driving-Munich-to-Berlin) — briefing a motorway run: several A-roads in one call, roadworks ahead, a fuel or charging stop, parking at the far end.
171
+ - [The commute that broke](https://github.com/mavrovde/viafrei-bridge/wiki/Use-case-The-commute-that-broke) — departures, regional disruption, and a station lift that is out, which is the difference between a step-free route existing and not.
172
+ - [An EV on a long weekend](https://github.com/mavrovde/viafrei-bridge/wiki/Use-case-An-EV-on-a-long-weekend) — charging by connector and power, low-emission-zone rules, what is around a stop.
173
+ - [Fleet and logistics briefings](https://github.com/mavrovde/viafrei-bridge/wiki/Use-case-Fleet-and-logistics-briefings) — a dispatcher's morning brief, watches that report a change instead of being polled, and the one licence rule that bites hardest here.
174
+ - [Building a local guide agent](https://github.com/mavrovde/viafrei-bridge/wiki/Use-case-Building-a-local-guide-agent) — a vague place to coordinates and back, and what OpenStreetMap's licence asks of you.
175
+
176
+ ## Why build on it
177
+
178
+ - **One endpoint instead of a stack of integrations.**
179
+ [SOURCES.md](SOURCES.md) lists **twenty-one sources from sixteen publishers** —
180
+ Autobahn GmbH, the national access point, Deutsche Bahn, DWD, BKG, GeoNames,
181
+ OpenStreetMap, MTS-K and the rest — each with its own format, its own release
182
+ rhythm and its own licence. They arrive here as one protocol and one set of
183
+ tools. (Count them in that table; one of the sixteen is us.)
184
+ - **The licence work is done and it travels with the answer.** Every result
185
+ carries the attribution line its sources require, and the ones with conditions
186
+ attached say so in the result itself — share-alike is flagged, and the MTS-K
187
+ purpose limit arrives as a sentence you are meant to show. You are not left to
188
+ work out what you owe whom.
189
+ - **It answers in the language of the question**, German or English, rather than
190
+ translating out of one house language.
191
+ - **It is a transport shim and nothing more.** The bridge holds no data and no
192
+ credentials, writes no file outside the OS temp directory, and makes no
193
+ decision about any answer. No telemetry, no analytics, no usage counter, no
194
+ update check — see [What it does not do](#what-it-does-not-do).
195
+ - **Failures are machine-readable.** One line on stderr and a distinct exit code
196
+ per cause, so a supervisor can tell "your network is down" from "you typed the
197
+ flag wrong" without parsing English.
198
+ - **Ask once, be told when it changes.** A watch turns polling into a
199
+ notification for as long as the conversation lives.
200
+
201
+ Free while ViaFrei stabilises, and the limits that exist are written down on this
202
+ page rather than discovered in production.
103
203
 
104
204
  ## Configuration
105
205
 
@@ -110,9 +210,30 @@ credentials, and it makes no decision about any answer.
110
210
  | `--timeout <ms>` | per-request timeout, default 30000. The event stream is never timed out |
111
211
  | `--version`, `--help` | print and exit |
112
212
 
113
- `VIAFREI_MCP_URL` and `VIAFREI_MCP_TIMEOUT_MS` do the same for clients that
114
- pass environment variables rather than arguments. A flag wins over the
115
- variable; the variable wins over the built-in default.
213
+ Many MCP clients can only pass an `env` block, not arguments, so every option
214
+ has an environment variable too:
215
+
216
+ | variable | same as |
217
+ |---|---|
218
+ | `VIAFREI_MCP_URL` | `--url` |
219
+ | `VIAFREI_MCP_HEADER` | `--header`. Several headers are separated by a **newline**, which can never appear in a header name or value, so nothing you might need to send is unrepresentable — a comma, a semicolon and a space all occur inside real header values |
220
+ | `VIAFREI_MCP_TIMEOUT_MS` | `--timeout` |
221
+
222
+ A flag wins over the variable; the variable wins over the built-in default. A
223
+ `--header` of the same name replaces one from the variable, and a `--header` of
224
+ a different name is added alongside it.
225
+
226
+ ```json
227
+ {
228
+ "mcpServers": {
229
+ "viafrei": {
230
+ "command": "npx",
231
+ "args": ["-y", "viafrei"],
232
+ "env": { "VIAFREI_MCP_HEADER": "Authorization: Bearer …" }
233
+ }
234
+ }
235
+ }
236
+ ```
116
237
 
117
238
  **There is no self-hosted ViaFrei.** The server is a hosted service, so `--url`
118
239
  is not a way to run your own — it is there for a proxy or gateway in front of
@@ -125,9 +246,13 @@ want.
125
246
  One line to stderr and an exit code that says what happened. No stack traces:
126
247
 
127
248
  ```
128
- viafrei: cannot reach https://mcp.viafrei.de/mcp: DNS lookup failed (EAI_AGAIN) - check your network connection; --url only if you relay through a proxy
249
+ viafrei: cannot reach https://example.invalid/mcp: host not found (DNS) (ENOTFOUND) - check your network connection; --url only if you relay through a proxy
129
250
  ```
130
251
 
252
+ That line is copied from a run, exit code 3. The same text also goes back to the
253
+ client as a JSON-RPC error, so an assistant can say what went wrong instead of
254
+ going quiet.
255
+
131
256
  | exit | meaning |
132
257
  |---|---|
133
258
  | `0` | clean shutdown (the client closed stdin, or sent SIGINT/SIGTERM) |
@@ -137,6 +262,27 @@ viafrei: cannot reach https://mcp.viafrei.de/mcp: DNS lookup failed (EAI_AGAIN)
137
262
  | `4` | the endpoint answered and this cannot continue: it refused (the line names the HTTP status), it forgot the session, it answered with something that is not MCP, or it redirected to another origin |
138
263
  | `5` | protocol version mismatch; the line names the version the server speaks |
139
264
 
265
+ **A slow call may be a retried call.** A `429`, `502`, `503` or `504` is tried
266
+ again **once** — after the delay the server asked for in `Retry-After`, or 250 ms
267
+ when it asked for none. A connection that fails outright rather than answering
268
+ (reset, broken pipe, socket or connect timeout) is likewise tried once more, after
269
+ the fixed 250 ms; there is no header to read on that path. Two cases are
270
+ deliberately not retried, because waiting would be worse than answering: a `429`
271
+ carrying no usable `Retry-After`, and any delay the server asks for that is longer
272
+ than your timeout. Both come straight back as the ordinary failure line with the
273
+ status in it. An event stream is never retried.
274
+
275
+ **Your timeout bounds each request, not the call.** It is attached per HTTP
276
+ request, so the second attempt gets a fresh one, and so does each hop of a
277
+ same-origin redirect. What you can rely on is per request: no single request
278
+ outlives the timeout, and a delay longer than the timeout is never waited out at
279
+ all. What follows from that is the arithmetic: two attempts plus a delay that may
280
+ itself be as long as the timeout is a worst case of about **three times** what you
281
+ set, and more than that if the endpoint redirects, since every hop is bounded
282
+ separately. **So no setting here caps the whole call** — if you need a hard
283
+ ceiling, enforce it on your side and treat the timeout as the per-request bound it
284
+ is.
285
+
140
286
  An established session is allowed to wobble — a dropped event stream is a
141
287
  warning, not an exit, and the bridge reconnects. It is not allowed to be dead
142
288
  in silence: several failures in a row with nothing succeeding in between end
@@ -166,9 +312,16 @@ licence breach rather than a style problem:
166
312
  - **MTS-K fuel prices are consumer information only.** No redistribution in any
167
313
  form — that includes aggregates, comparisons, price tables and anything
168
314
  derived. Answer the person who asked; do not build a product out of it.
169
- - **DELFI public-transport data is CC BY-SA 4.0.** Share-alike travels with
170
- anything derived from it, and it must not be blended into a result under a
171
- different licence.
315
+ - **DELFI public-transport data is Creative Commons
316
+ Attribution-ShareAlike.** Share-alike travels with anything you *derive* from
317
+ it — recompute it, reshape it, rearrange it, build a delay table out of it, and
318
+ that is Adapted Material you must license under BY-SA (art. 1(a) names material
319
+ "translated, altered, arranged, transformed, or otherwise modified"). Merely **showing** it beside
320
+ another source's data is an aggregation, and puts no obligation on the other
321
+ source; an earlier version of this page said otherwise, which told you a
322
+ licence forbids something it permits. The catalogue record for the realtime
323
+ feed names no licence version, so do not rely on one for a derivative —
324
+ [SOURCES.md](SOURCES.md) has the detail and the open question.
172
325
 
173
326
  Everything else — where each answer comes from, and what each licence asks of
174
327
  you — is in [SOURCES.md](SOURCES.md). See also [NOTICE](NOTICE) and
@@ -186,9 +339,15 @@ This is said plainly so nobody spends an evening looking for the server code.
186
339
 
187
340
  ## Contributing
188
341
 
189
- Yes, please — see [CONTRIBUTING.md](CONTRIBUTING.md). Issues and discussions
190
- are open. The bridge is small and self-contained, which is exactly what makes
191
- it a reasonable thing to send a first patch to.
342
+ Yes, please — see [CONTRIBUTING.md](CONTRIBUTING.md) and
343
+ [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). Issues and discussions are open. The
344
+ bridge is small and self-contained, which is exactly what makes it a reasonable
345
+ thing to send a first patch to.
346
+
347
+ **One issue template is worth knowing about before you need it:** *the answer was
348
+ wrong or useless*. A tool that fails is something the server sees; a tool that
349
+ answers confidently with the wrong thing is not. That report is the one thing we
350
+ cannot get any other way.
192
351
 
193
352
  ## Security
194
353