clone

one developer profile for links and private tcp services, reached peer to peer over iroh with no open ports

main
devsite / web / index.html
index.html 7.6K
1<!doctype html>2<html lang="en">3<head>4<meta charset="utf-8">5<meta name="viewport" content="width=device-width, initial-scale=1">6<title>dev.site</title>7<!-- Pico first, this site's few additions second, and the profile's own theme8 last. Last is necessary but not sufficient: Pico's palette sits on9 :root:not([data-theme=dark]), which is specificity (0,2,0), so the theme10 block matches it with :root[data-profile="…"] and wins on order. The profile11 stream writes this block before it morphs the profile markup. -->12<link rel="stylesheet" href="/vendor/pico.min.css">13<link rel="stylesheet" href="/site.css">14<style id="profile-theme"></style>15</head>16<body>1718<header class="container">19 <nav>20 <ul>21 <li>22 <a href="/" class="contrast brand" aria-label="dev.site">23 <svg width="24" height="24" viewBox="2 2 20 20" aria-hidden="true"24 xmlns="http://www.w3.org/2000/svg">25 <path d="M16,15,12,7,8,15Zm-1,0L11.5,8"26 style="fill:none;stroke:var(--pico-primary);stroke-linecap:round;stroke-linejoin:round;stroke-width:2"></path>27 <circle id="dev-logo-ring" cx="12" cy="12" r="9"28 style="fill:none;stroke:var(--devsite-logo-ring, rgb(0,0,0));stroke-linecap:round;stroke-linejoin:round;stroke-width:2"></circle>29 </svg>30 <strong>dev.site</strong>31 </a>32 </li>33 </ul>34 <ul id="session"></ul>35 </nav>36</header>3738<template id="home-template">39 <article class="home-intro">40 <hgroup>41 <h1 aria-label="dev.site">42 <svg class="home-wordmark" viewBox="0 0 64.8 10" aria-hidden="true"43 xmlns="http://www.w3.org/2000/svg">44 <path d="M4.8 0H6V1H4.8ZM44.4 0H45.6V1H44.4ZM4.8 1H6V2H4.8ZM52.8 1H54V2H52.8ZM4.8 2H6V3H4.8ZM52.8 2H54V3H52.8ZM1.2 3H6V4H1.2ZM9.6 3H13.2V4H9.6ZM16.8 3H18V4H16.8ZM21.6 3H22.8V4H21.6ZM34.8 3H38.4V4H34.8ZM42 3H45.6V4H42ZM50.4 3H56.4V4H50.4ZM60 3H63.6V4H60ZM0 4H2.4V5H0ZM3.6 4H6V5H3.6ZM8.4 4H10.8V5H8.4ZM13.2 4H14.4V5H13.2ZM16.8 4H18V5H16.8ZM21.6 4H22.8V5H21.6ZM33.6 4H34.8V5H33.6ZM38.4 4H39.6V5H38.4ZM44.4 4H45.6V5H44.4ZM52.8 4H54V5H52.8ZM58.8 4H61.2V5H58.8ZM63.6 4H64.8V5H63.6ZM0 5H1.2V6H0ZM4.8 5H6V6H4.8ZM8.4 5H9.6V6H8.4ZM13.2 5H14.4V6H13.2ZM18 5H19.2V6H18ZM20.4 5H21.6V6H20.4ZM33.6 5H34.8V6H33.6ZM44.4 5H45.6V6H44.4ZM52.8 5H54V6H52.8ZM58.8 5H60V6H58.8ZM63.6 5H64.8V6H63.6ZM0 6H1.2V7H0ZM4.8 6H6V7H4.8ZM8.4 6H14.4V7H8.4ZM18 6H19.2V7H18ZM20.4 6H21.6V7H20.4ZM34.8 6H38.4V7H34.8ZM44.4 6H45.6V7H44.4ZM52.8 6H54V7H52.8ZM58.8 6H64.8V7H58.8ZM0 7H1.2V8H0ZM4.8 7H6V8H4.8ZM8.4 7H9.6V8H8.4ZM18 7H21.6V8H18ZM38.4 7H39.6V8H38.4ZM44.4 7H45.6V8H44.4ZM52.8 7H54V8H52.8ZM58.8 7H60V8H58.8ZM0 8H2.4V9H0ZM3.6 8H6V9H3.6ZM8.4 8H10.8V9H8.4ZM13.2 8H14.4V9H13.2ZM19.2 8H20.4V9H19.2ZM27.6 8H28.8V9H27.6ZM33.6 8H34.8V9H33.6ZM38.4 8H39.6V9H38.4ZM44.4 8H45.6V9H44.4ZM52.8 8H54V9H52.8ZM58.8 8H61.2V9H58.8ZM63.6 8H64.8V9H63.6ZM1.2 9H6V10H1.2ZM9.6 9H13.2V10H9.6ZM19.2 9H20.4V10H19.2ZM27.6 9H28.8V10H27.6ZM34.8 9H38.4V10H34.8ZM42 9H48V10H42ZM54 9H56.4V10H54ZM60 9H63.6V10H60Z"></path>45 </svg>46 </h1>47 <p>Links and private services on one profile page.</p>48 </hgroup>49 <p>dev.site gives you a page at <code>dev.site/@you</code>. It holds links and services.50 A service is a TCP port on one of your machines, for example a database or a development51 server. You choose who can see and use each item.</p>52 </article>5354 <section aria-labelledby="example-title">55 <h2 id="example-title">An example</h2>56 <p>You run Postgres on port 5432 on your laptop. A teammate needs to query it. On your57 laptop, name the port and keep the daemon running:</p>58 <pre><code>devsite service host 5432 --name postgres59devsite daemon run</code></pre>60 <p>Share <code>postgres</code> with your teammate on the dashboard. They accept the share,61 get a ticket from your profile, and run:</p>62 <pre><code>devsite connect dst_***63# connected to postgres64# listening on 127.0.0.1:43127</code></pre>65 <p>Their <code>psql</code> now connects to <code>127.0.0.1:43127</code>. The data goes66 from their machine to yours. Your laptop does not open a port to the internet.</p>67 <p>Links work in the same way, without the daemon:</p>68 <pre><code>devsite link set --name docs --url https://docs.example.com --public</code></pre>69 </section>7071 <section aria-labelledby="install-title">72 <h2 id="install-title">Install</h2>73 <div data-install-control></div>74 <p>Then sign in, create a machine ticket on the dashboard, and use it on this machine:</p>75 <pre><code>devsite login dmt_***</code></pre>76 </section>7778 <article aria-labelledby="sees-title">79 <h2 id="sees-title">What dev.site sees</h2>80 <p>dev.site handles sign-in, profiles, shares, and permission for each connection. It81 does not carry your service data. It does not know your local port numbers.</p>82 <figure class="sequence">83 <header aria-hidden="true">84 <strong>host cli</strong>85 <strong>dev.site</strong>86 <strong>approved user</strong>87 </header>88 <div>89 <i aria-hidden="true"></i><i aria-hidden="true"></i><i aria-hidden="true"></i>90 <ol aria-label="A host proves control of and registers its bound endpoint, then receives current service authorizations from dev.site. An approved user redeems a one-use ticket into a client-bound session, requests a signed capability for each connection, then opens an end-to-end encrypted Iroh QUIC stream to the host service.">91 <li><span>prove + register bound endpoint</span></li>92 <li><span>sync authorization snapshot</span></li>93 <li><span>redeem one-use ticket + client key</span></li>94 <li><span>client-bound session</span></li>95 <li><span>request one-stream capability</span></li>96 <li><span>signed capability + daemon id</span></li>97 <li><span>end-to-end encrypted Iroh QUIC</span></li>98 </ol>99 </div>100 </figure>101 <nav aria-label="Security concept guides">102 <ul>103 <li><a href="/concepts.html#tickets">Tickets</a></li>104 <li><a href="/concepts.html#keys">Keys</a></li>105 <li><a href="/concepts.html#protocol">ALPN</a></li>106 </ul>107 </nav>108 </article>109110 <details>111 <summary>Give an agent limited access</summary>112 <p>A sandboxed agent can ask for a service by name. You see the request and approve it.113 The agent then gets a short-lived grant for that one service. You can revoke it.</p>114 <pre><code>devsite access request postgres --request request.json --key requester.key</code></pre>115 <p>On your machine, find the service, check the plan, and approve it.</p>116 <pre><code>devsite access resolve postgres117devsite access grant --request request.json --plan118devsite access grant --request request.json --approved-plan dsp_***</code></pre>119 <p>The agent connects with its grant.</p>120 <pre><code>devsite access connect dss_*** --key requester.key</code></pre>121 </details>122123 <details>124 <summary>Run the daemon at startup</summary>125 <p>Start <code>devsite daemon run</code> with Homebrew services or systemd. Your services126 then stay available after a restart.</p>127 </details>128</template>129130<main class="container" id="main" aria-busy="true"></main>131132<footer class="container" id="page-footer" hidden></footer>133134<dialog id="service-ticket-dialog">135 <article>136 <header>137 <button aria-label="Close" rel="prev" id="service-ticket-close"></button>138 <strong id="service-ticket-title"></strong>139 </header>140 <div id="service-ticket-body"></div>141 <p id="service-ticket-status"></p>142 </article>143</dialog>144145<script type="module" src="/pkg/app.js"></script>146</body>147</html>