1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152 | <!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>portofel — the serverless p2p wallet</title>
<style>
:root {
--bg: #05060f; --panel: #0b0e1f; --panel-2: #10142a; --border: #1a2140;
--text: #d8e6ff; --muted: #6b7aa8; --accent: #a3be8c;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg); color: var(--text);
font: 16px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
padding: 40px 20px 80px;
}
main { max-width: 780px; margin: 0 auto; }
h1 { font-size: 34px; letter-spacing: 4px; color: var(--accent); text-shadow: 0 0 18px var(--accent); }
.tag { color: var(--muted); margin: 6px 0 30px; font-size: 17px; }
h2 { margin: 44px 0 12px; font-size: 22px; color: var(--accent); }
h3 { margin: 24px 0 8px; font-size: 17px; }
p, li { color: #b9c6dd; }
ul { padding-left: 22px; margin: 10px 0; }
li { margin: 5px 0; }
a { color: var(--accent); }
pre {
background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
padding: 14px 16px; overflow-x: auto; margin: 12px 0;
font: 13.5px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
}
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 14px;
background: var(--panel-2); border-radius: 5px; padding: 1px 6px; }
pre code { background: none; padding: 0; }
.card {
background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
padding: 18px 20px; margin: 14px 0;
}
.btns { display: flex; gap: 10px; flex-wrap: wrap; margin: 18px 0 6px; }
.btn {
display: inline-block; padding: 10px 18px; border-radius: 8px; text-decoration: none;
background: #101a10; border: 1px solid #2e4a2e; color: var(--accent);
letter-spacing: 1px; font-size: 14px;
}
.btn:hover { background: #1a2e1a; box-shadow: 0 0 14px #a3be8c44; }
footer { margin-top: 60px; color: var(--muted); font-size: 13px; }
</style>
</head>
<body>
<!-- TODO: add ro/hu blocks + the language nav in the pattern of
game1/site/index.html (data-lang-block + localStorage). -->
<main>
<h1>portofel</h1>
<p class="tag">all your money, from every bank you use, on one screen — this page is also the git repo.</p>
<div class="btns">
<a class="btn" href="https://portofel.ardegazu.ro/">open the app</a>
<a class="btn" href="browse/index.html">browse the code</a>
<a class="btn" href="#clone">clone the repo</a>
</div>
<h2 id="clone">Clone</h2>
<p>This site serves the repository over git's dumb-HTTP protocol as plain static
files (plain files — there is no git server here, or any server at all):</p>
<pre><code>git clone https://git.ardegazu.ro/portofel.git
cd portofel</code></pre>
<h2>What it is</h2>
<p>portofel is the suite's wallet: one card per bank you use, the balance big,
the issuer visible, and a send button that pays any friend who holds an
account there. A balance is a fold over that bank's encrypted log — this app
holds your bank links, replicates each log as an ordinary
<a href="https://git.ardegazu.ro/banca.git">banca</a> member, and computes
every number locally. There is deliberately no total across banks: different
issuers are different currencies, and the wallet never pretends otherwise.
Installable PWA — works saved to an iOS home screen, safe-area aware.</p>
<h2>How it works</h2>
<ul>
<li><strong>No server, no room of its own.</strong> Browsers are
<a href="https://libp2p.io">libp2p</a> peers: they meet through a
circuit-relay-v2 node, discover each other via gossipsub, and upgrade to
direct WebRTC. Each bank is its own encrypted room, keyed by the
capability link you hold — the link never leaves the browser, and this
wallet opens one headless bank session per link.</li>
<li><strong>Everything is end-to-end encrypted.</strong> Every peer pair is
noise-encrypted end-to-end (even while frames still cross the relay), and
bank membership is proved with a sealed hello derived from the link — an
undecryptable hello means you're not in the bank. A sealed offline
mailbox carries entries between members who are never online together.</li>
<li><strong>A payment is banca's payment.</strong> portofel signs the same
wallet-kit order banca signs, appends it to the bank's log, and shows it
with the same honest words: provisional until the banker acknowledges it,
final after — never anything dressed up as done that is not.</li>
<li><strong>Hosting without an origin server.</strong> The client is a
static build served as plain files — there is no backend anywhere. This
page you're reading works the same way.</li>
</ul>
<h2>What's in the repo</h2>
<div class="card">
<h3>client/src/portofel/app/ — the wallet</h3>
<p>ClojureScript, no framework: the bank directory (<code>banks.cljs</code>),
the headless bank session (<code>bankroom.cljs</code> — banca's fold,
compiled from the sha-pinned banca sources, never a transcription), the
store, and the one-column card UI with its send sheet.</p>
</div>
<div class="card">
<h3>the kits — compiled from source, off the classpath</h3>
<p>The shared rooms core (<code>ardegazu-rooms-kit</code>), the suite
identity (<code>ardegazu-id-kit</code>), friends and presence
(<code>ardegazu-social-kit</code>), the money artifacts
(<code>ardegazu-wallet-kit</code>) and banca's pure namespaces — all
sha-pinned npm git deps whose ClojureScript compiles into this build.</p>
</div>
<div class="card">
<h3>deploy/ — the publish pipeline</h3>
<p><code>publish-repo.sh</code> builds this very site: landing page + bare
mirror exploded to loose objects (static file servers and git's dumb-HTTP protocol
agree on "every path must exist"), with an anonymity gate that refuses to
publish identity-bearing bytes.</p>
</div>
<h2>Run it yourself</h2>
<pre><code>cd client && npm install && npm run dev # :5173</code></pre>
<p>Deploy: <code>npm run build</code>, then publish <code>client/dist/</code> as
static files anywhere — IPFS, a CDN, a folder behind nginx. It needs nothing but
a libp2p circuit relay it's allowed to talk to (see
<code>src/portofel/config.cljs</code>).</p>
<h2>Guarantees & limits, honestly</h2>
<ul>
<li>The relay operator sees connection metadata, opaque room ids, and frame
sizes/timing. Not content, not names, not amounts.</li>
<li>A bank link is the whole capability, and it is irreversible: anyone who
holds it reads every payment between every member of that bank, forever.
Forgetting a bank here removes it from your devices — it un-discloses
nothing.</li>
<li>A payment is provisional until the banker acknowledges it, and the
screen says so; the banker of each bank is who you are trusting, exactly
as in banca.</li>
<li>MIT licensed. Built on WebRTC and WebCrypto.</li>
</ul>
<footer>
Live: <a href="https://portofel.ardegazu.ro/">portofel.ardegazu.ro</a> ·
this repo: <code>git clone https://git.ardegazu.ro/portofel.git</code> ·
built on <a href="https://libp2p.io">js-libp2p</a>
</footer>
</main>
</body>
</html>
|