Smart SSH, HTTPS and MySQL bastion that needs no client-side software
Go to file
dependabot[bot] 9ca95b7eb7
Bump the version-bumps group across 1 directory with 4 updates (#1061)
Bumps the version-bumps group with 4 updates in the /warpgate-web
directory:
[eslint-plugin-svelte](https://github.com/sveltejs/eslint-plugin-svelte),
[ua-parser-js](https://github.com/faisalman/ua-parser-js),
[vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) and
[vite-plugin-checker](https://github.com/fi3ework/vite-plugin-checker).

Updates `eslint-plugin-svelte` from 2.43.0 to 2.44.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sveltejs/eslint-plugin-svelte/releases">eslint-plugin-svelte's
releases</a>.</em></p>
<blockquote>
<h2>eslint-plugin-svelte@2.44.0</h2>
<h3>Minor Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/sveltejs/eslint-plugin-svelte/pull/841">#841</a>
<a
href="85053a1af2"><code>85053a1</code></a>
Thanks <a href="https://github.com/jrmajor"><code>@​jrmajor</code></a>!
- feat: add config option for foreign elements in
<code>svelte/html-self-closing</code> rule</li>
</ul>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/sveltejs/eslint-plugin-svelte/pull/853">#853</a>
<a
href="690c04e5ce"><code>690c04e</code></a>
Thanks <a
href="https://github.com/ota-meshi"><code>@​ota-meshi</code></a>! - fix:
update svelte-eslint-parser to 0.41.1</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c0658db864"><code>c0658db</code></a>
chore: release eslint-plugin-svelte (<a
href="https://redirect.github.com/sveltejs/eslint-plugin-svelte/issues/852">#852</a>)</li>
<li><a
href="690c04e5ce"><code>690c04e</code></a>
fix: update svelte-eslint-parser to 0.41.1 (<a
href="https://redirect.github.com/sveltejs/eslint-plugin-svelte/issues/853">#853</a>)</li>
<li><a
href="bd9e6ccc4e"><code>bd9e6cc</code></a>
chore: update deps (<a
href="https://redirect.github.com/sveltejs/eslint-plugin-svelte/issues/854">#854</a>)</li>
<li><a
href="85053a1af2"><code>85053a1</code></a>
feat: add config option for foreign elements in
<code>svelte/html-self-closing</code> ru...</li>
<li><a
href="d117d7fc15"><code>d117d7f</code></a>
chore(deps): update dependency eslint-plugin-jsdoc to v50</li>
<li><a
href="dfed5cdaf4"><code>dfed5cd</code></a>
chore(deps): update dependency eslint-plugin-jsdoc to v49</li>
<li><a
href="f14a8ee4dc"><code>f14a8ee</code></a>
chore(deps): update dependency eslint-typegen to ^0.3.0</li>
<li><a
href="6bd4e13a3e"><code>6bd4e13</code></a>
chore: move playground to <code>eslint-online-playground</code> (<a
href="https://redirect.github.com/sveltejs/eslint-plugin-svelte/issues/829">#829</a>)</li>
<li>See full diff in <a
href="https://github.com/sveltejs/eslint-plugin-svelte/compare/eslint-plugin-svelte@2.43.0...eslint-plugin-svelte@2.44.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `ua-parser-js` from 1.0.38 to 1.0.39
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/faisalman/ua-parser-js/blob/1.0.39/changelog.md">ua-parser-js's
changelog</a>.</em></p>
<blockquote>
<h2>Version 0.7.39 / 1.0.39</h2>
<ul>
<li>Add new feature: executable command using <code>npx ua-parser-js
&quot;[INSERT-UA-HERE]&quot;</code></li>
<li>Add new browser: Helio, Pico Browser, Wolvic</li>
<li>Add new device vendor: itel, Nothing, TCL</li>
<li>Improve browser detection: ICEBrowser, Klar, QQBrowser, Quark,
Rekonq, Sleipnir</li>
<li>Improve device detection: Xiaomi Pro, Amazon Echo Show, Samsung
Galaxy Watch</li>
<li>Removed from browser: Viera</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ab77baf6de"><code>ab77baf</code></a>
Bump version 1.0.39 (mirror of 0.7.39)</li>
<li><a
href="2ad60d4d8e"><code>2ad60d4</code></a>
Bump version 0.7.39</li>
<li><a
href="c25e22fa87"><code>c25e22f</code></a>
Backport - Fix <a
href="https://redirect.github.com/faisalman/ua-parser-js/issues/743">#743</a>
- Improve device detection for Xiaomi</li>
<li><a
href="a9315821da"><code>a931582</code></a>
Backport - Add new device vendor: itel</li>
<li><a
href="33eb27d205"><code>33eb27d</code></a>
Backport - Improve detection: Amazon Echo Show devices</li>
<li><a
href="a77184ffa9"><code>a77184f</code></a>
Backport - Improve detection: recognize Samsung Galaxy Watch devices as
`wear...</li>
<li><a
href="b6a92c685e"><code>b6a92c6</code></a>
Backport - Add new device vendor: TCL</li>
<li><a
href="4303c32d84"><code>4303c32</code></a>
Backport - Add new vendor: Nothing</li>
<li><a
href="bef7c777a7"><code>bef7c77</code></a>
Backport - Improve browser detection for Quark (<a
href="https://redirect.github.com/faisalman/ua-parser-js/issues/737">#737</a>)</li>
<li><a
href="06e3c8de6a"><code>06e3c8d</code></a>
Backport - Add new browser: Helio</li>
<li>Additional commits viewable in <a
href="https://github.com/faisalman/ua-parser-js/compare/1.0.38...1.0.39">compare
view</a></li>
</ul>
</details>
<br />

Updates `vite` from 5.4.3 to 5.4.6
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/blob/v5.4.6/packages/vite/CHANGELOG.md">vite's
changelog</a>.</em></p>
<blockquote>
<h2><!-- raw HTML omitted -->5.4.6 (2024-09-16)<!-- raw HTML omitted
--></h2>
<ul>
<li>fix: avoid DOM Clobbering gadget in
<code>getRelativeUrlFromDocument</code> (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18115">#18115</a>)
(<a
href="179b17773c">179b177</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/18115">#18115</a></li>
<li>fix: fs raw query (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18112">#18112</a>)
(<a
href="6820bb3b9a">6820bb3</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/18112">#18112</a></li>
</ul>
<h2><!-- raw HTML omitted -->5.4.5 (2024-09-13)<!-- raw HTML omitted
--></h2>
<ul>
<li>fix(preload): backport <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18098">#18098</a>,
throw error preloading module as well (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18099">#18099</a>)
(<a
href="faa2405e5d">faa2405</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/18098">#18098</a>
<a
href="https://redirect.github.com/vitejs/vite/issues/18099">#18099</a></li>
</ul>
<h2><!-- raw HTML omitted -->5.4.4 (2024-09-11)<!-- raw HTML omitted
--></h2>
<ul>
<li>fix: backport <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/17997">#17997</a>,
ensure req.url matches moduleByEtag URL to avoid incorrect 304 (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18078">#18078</a>)
(<a
href="74a79c53b2">74a79c5</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/17997">#17997</a>
<a
href="https://redirect.github.com/vitejs/vite/issues/18078">#18078</a></li>
<li>fix: backport <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18063">#18063</a>,
allow scanning exports from <code>script module</code> in svelte (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18077">#18077</a>)
(<a
href="d90ba40474">d90ba40</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/18063">#18063</a>
<a
href="https://redirect.github.com/vitejs/vite/issues/18077">#18077</a></li>
<li>fix(preload): backport <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18046">#18046</a>,
allow ignoring dep errors (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18076">#18076</a>)
(<a
href="8760293d68">8760293</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/18046">#18046</a>
<a
href="https://redirect.github.com/vitejs/vite/issues/18076">#18076</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f9691767ad"><code>f969176</code></a>
release: v5.4.6</li>
<li><a
href="179b17773c"><code>179b177</code></a>
fix: avoid DOM Clobbering gadget in
<code>getRelativeUrlFromDocument</code> (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18115">#18115</a>)</li>
<li><a
href="6820bb3b9a"><code>6820bb3</code></a>
fix: fs raw query (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18112">#18112</a>)</li>
<li><a
href="37881e7198"><code>37881e7</code></a>
release: v5.4.5</li>
<li><a
href="faa2405e5d"><code>faa2405</code></a>
fix(preload): backport <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18098">#18098</a>,
throw error preloading module as well (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18099">#18099</a>)</li>
<li><a
href="54c55dbffc"><code>54c55db</code></a>
release: v5.4.4</li>
<li><a
href="74a79c53b2"><code>74a79c5</code></a>
fix: backport <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/17997">#17997</a>,
ensure req.url matches moduleByEtag URL to avoid incorr...</li>
<li><a
href="d90ba40474"><code>d90ba40</code></a>
fix: backport <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18063">#18063</a>,
allow scanning exports from <code>script module</code> in svelte
(...</li>
<li><a
href="8760293d68"><code>8760293</code></a>
fix(preload): backport <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18046">#18046</a>,
allow ignoring dep errors (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/18076">#18076</a>)</li>
<li>See full diff in <a
href="https://github.com/vitejs/vite/commits/v5.4.6/packages/vite">compare
view</a></li>
</ul>
</details>
<br />

Updates `vite-plugin-checker` from 0.7.2 to 0.8.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/fi3ework/vite-plugin-checker/releases">vite-plugin-checker's
releases</a>.</em></p>
<blockquote>
<h2>vite-plugin-checker@0.8.0</h2>
<h3>   🚨 Breaking Changes</h3>
<ul>
<li>
<p><strong>vue-tsc</strong>: Updated createVueLanguagePlugin syntax,
version pin  -  by <a
href="https://github.com/daniluk4000"><code>@​daniluk4000</code></a> and
<strong>drodichkin</strong> in <a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/398">fi3ework/vite-plugin-checker#398</a>
<a
href="https://github.com/fi3ework/vite-plugin-checker/commit/5f5b92a"><!--
raw HTML omitted -->(5f5b9)<!-- raw HTML omitted --></a></p>
<p>Now, vite-plugin-checker requires <code>&quot;vue-tsc&quot;:
&quot;~2.1.6&quot;</code>.</p>
</li>
</ul>
<h3>   🚀 Features</h3>
<ul>
<li>Advance typescript teminal consolelog text color  -  by <a
href="https://github.com/jaceechan"><code>@​jaceechan</code></a> in <a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/393">fi3ework/vite-plugin-checker#393</a>
<a
href="https://github.com/fi3ework/vite-plugin-checker/commit/aa3d413"><!--
raw HTML omitted -->(aa3d4)<!-- raw HTML omitted --></a></li>
</ul>
<h3>   🐞 Bug Fixes</h3>
<ul>
<li><strong>ui</strong>: Use a Vue key so file paths update correctly
 -  by <a
href="https://github.com/artursapek"><code>@​artursapek</code></a> in <a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/389">fi3ework/vite-plugin-checker#389</a>
<a
href="https://github.com/fi3ework/vite-plugin-checker/commit/fcbb687"><!--
raw HTML omitted -->(fcbb6)<!-- raw HTML omitted --></a></li>
</ul>
<h5>    <a
href="https://github.com/fi3ework/vite-plugin-checker/compare/vite-plugin-checker@0.7.2...vite-plugin-checker@0.8.0">View
changes on GitHub</a></h5>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="19c58e3dba"><code>19c58e3</code></a>
v0.8.0</li>
<li><a
href="5179e572d8"><code>5179e57</code></a>
ci: drop changesets, use manual triggered version release</li>
<li><a
href="fcbb6875d5"><code>fcbb687</code></a>
fix(ui): use a Vue key so file paths update correctly (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/389">#389</a>)</li>
<li><a
href="aa3d413148"><code>aa3d413</code></a>
feat: advance typescript teminal consolelog text color (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/393">#393</a>)</li>
<li><a
href="5f5b92a914"><code>5f5b92a</code></a>
fix(vue-tsc): updated createVueLanguagePlugin syntax, version pin (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/398">#398</a>)</li>
<li><a
href="b2c32367cf"><code>b2c3236</code></a>
chore(deps): update dependency <code>@​types/babel</code>__code-frame to
^7.0.6 (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/376">#376</a>)</li>
<li><a
href="8827a1e083"><code>8827a1e</code></a>
chore(deps): update dependency <code>@​types/debug</code> to ^4.1.12 (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/377">#377</a>)</li>
<li><a
href="152eafb622"><code>152eafb</code></a>
chore(deps): update dependency <code>@​tsconfig/esm</code> to ^1.0.5 (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/374">#374</a>)</li>
<li><a
href="3a21a2315a"><code>3a21a23</code></a>
chore(deps): update dependency <code>@​tsconfig/strictest</code> to
^2.0.5 (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/375">#375</a>)</li>
<li><a
href="5d01c8615c"><code>5d01c86</code></a>
chore(deps): replace dependency npm-run-all with npm-run-all2 ^5.0.0 (<a
href="https://redirect.github.com/fi3ework/vite-plugin-checker/issues/371">#371</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/fi3ework/vite-plugin-checker/compare/vite-plugin-checker@0.7.2...vite-plugin-checker@0.8.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 08:12:39 +02:00
.cargo Validate a TOTP code before saving it (#1055) 2024-09-11 09:30:02 +02:00
.github Group dependabot version bump prs if they are minor or patch bumps (#1049) 2024-09-05 16:53:45 +02:00
docker bumped rust in docker image 2024-07-16 12:44:23 +02:00
docs added readme banner 2022-08-08 16:03:35 +02:00
oidc-test OIDC RP-initiated logout (SSO single logout) support (#992) 2024-09-10 23:16:42 +02:00
tests word 2024-03-23 21:54:21 +01:00
warpgate Bump version: 0.10.1 → 0.10.2 2024-08-14 22:59:52 +02:00
warpgate-admin Bump version: 0.10.1 → 0.10.2 2024-08-14 22:59:52 +02:00
warpgate-common Bump version: 0.10.1 → 0.10.2 2024-08-14 22:59:52 +02:00
warpgate-core fixed SSO authentication getting incorrectly rejected when user has both an "any provider" and a provider specific SSO credential 2024-09-10 23:12:44 +02:00
warpgate-database-protocols Bump version: 0.10.1 → 0.10.2 2024-08-14 22:59:52 +02:00
warpgate-db-entities Bump version: 0.10.1 → 0.10.2 2024-08-14 22:59:52 +02:00
warpgate-db-migrations Bump version: 0.10.1 → 0.10.2 2024-08-14 22:59:52 +02:00
warpgate-protocol-http OIDC RP-initiated logout (SSO single logout) support (#992) 2024-09-10 23:16:42 +02:00
warpgate-protocol-mysql Bump version: 0.10.1 → 0.10.2 2024-08-14 22:59:52 +02:00
warpgate-protocol-ssh Bump version: 0.10.1 → 0.10.2 2024-08-14 22:59:52 +02:00
warpgate-sso OIDC RP-initiated logout (SSO single logout) support (#992) 2024-09-10 23:16:42 +02:00
warpgate-web Bump the version-bumps group across 1 directory with 4 updates (#1061) 2024-09-18 08:12:39 +02:00
.all-contributorsrc Add @SheaSmith as a contributor 2024-03-23 21:59:52 +01:00
.bumpversion.cfg Bump version: 0.10.1 → 0.10.2 2024-08-14 22:59:52 +02:00
.dockerignore Updated Dockerfile & setup 2022-07-05 21:32:05 +02:00
.env import 2022-04-10 22:58:58 +02:00
.flake8 added e2e tests 2022-08-14 12:36:49 +02:00
.gitignore Validate a TOTP code before saving it (#1055) 2024-09-11 09:30:02 +02:00
Cargo.lock Validate a TOTP code before saving it (#1055) 2024-09-11 09:30:02 +02:00
Cargo.toml bumped rust 2024-01-23 12:05:59 +01:00
clippy.toml added cranky and removed all .unwrap() usages 2022-07-23 21:31:35 +02:00
Cranky.toml deny clippy::indexing_slicing 2022-07-23 21:53:21 +02:00
Cross.toml build updates 2022-11-22 00:52:43 +01:00
deny.toml Revert "use workspace-level dependencies" 2022-07-06 09:24:06 +02:00
justfile Validate a TOTP code before saving it (#1055) 2024-09-11 09:30:02 +02:00
LICENSE Update LICENSE 2022-04-14 11:14:56 +02:00
README.md Update README.md 2024-09-04 17:03:37 +02:00
rust-toolchain bump rust some more 2024-07-16 10:40:27 +02:00
rustfmt.toml sorted imports 2022-07-15 20:27:33 +02:00
sonar-project.properties added e2e tests 2022-08-14 12:36:49 +02:00



GitHub All Releases     Discord


Warpgate is a smart SSH, HTTPS and MySQL bastion host for Linux that doesn't need special client apps.

  • Set it up in your DMZ, add user accounts and easily assign them to specific hosts and URLs within the network.
  • Warpgate will record every session for you to view (live) and replay later through a built-in admin web UI.
  • Not a jump host - forwards your connections straight to the target instead.
  • Native 2FA and SSO support (TOTP & OpenID Connect)
  • Single binary with no dependencies.
  • Written in 100% safe Rust.

Getting started & downloads

image
image image

Project Status

The project is currently in alpha stage and is gathering community feedback. See the official roadmap for the upcoming features.

In particular, we're working on:

  • Requesting admin approvals for sessions
  • Support for tunneling PostgreSQL connections,
  • and much more.

How it works

Warpgate is a service that you deploy on the bastion/DMZ host, which will accept SSH, HTTPS and MySQL connections and provide an (optional) web admin UI.

Run warpgate setup to interactively generate a config file, including port bindings. See Getting started for details.

It receives connections with specifically formatted credentials, authenticates the user locally, connects to the target itself, and then connects both parties together while (optionally) recording the session.

When connecting through HTTPS, Warpgate presents a selection of available targets, and will then proxy all traffic in a session to the selected target. You can switch between targets at any time.

You manage the target and user lists and assign them to each other through the admin UI, and the session history is stored in an SQLite database (default: in /var/lib/warpgate).

You can also use the admin web interface to view the live session list, review session recordings, logs and more.

Contributing / building from source

  • You'll need Rust, NodeJS and Yarn
  • Clone the repo
  • Just is used to run tasks - install it: cargo install just
  • Install the admin UI deps: just yarn
  • Build the frontend: just yarn build
  • Build Warpgate: cargo build (optionally --release)

The binary is in target/{debug|release}.

Tech stack

  • Rust 🦀
    • HTTP: poem-web
    • Database: SQLite via sea-orm + sqlx
    • SSH: russh
  • Typescript
    • Svelte
    • Bootstrap

Backend API

  • Warpgate admin and user facing APIs use autogenerated OpenAPI schemas and SDKs. To update the SDKs after changing the query/response structures, run just openapi-all.

Contributors

Thanks goes to these wonderful people (emoji key):

Eugeny
Eugeny

💻
Spencer Heywood
Spencer Heywood

💻
Andreas Piening
Andreas Piening

💻
Niklas
Niklas

💻
Nooblord
Nooblord

💻
Shea Smith
Shea Smith

💻

This project follows the all-contributors specification. Contributions of any kind welcome!