summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile7
-rw-r--r--README2
-rw-r--r--cmd/buthd/main.go (renamed from cmd/webshopd/main.go)1
-rw-r--r--doc/arch.dot18
-rw-r--r--doc/arch.md35
-rw-r--r--doc/arch.pngbin0 -> 41371 bytes
-rw-r--r--doc/auth.md50
-rw-r--r--doc/libs.md6
-rw-r--r--doc/notation.md2
10 files changed, 121 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 0314984..ef2d82e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
bin/
+doc/.obsidian/
.hugo_build.lock
public/
diff --git a/Makefile b/Makefile
index 27685dc..5f7f1bd 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-all: public bin/webshopd
+all: doc public bin/webshopd
public:
hugo build
@@ -6,5 +6,10 @@ public:
bin/%: cmd/%
go build -o $@ ./$<
+doc: doc/arch.png
+
+doc/arch.png: doc/arch.dot
+ dot -Tpng <$< >$@
+
clean:
rm -rf bin public
diff --git a/README b/README
index 1e7bbb8..0602dd3 100644
--- a/README
+++ b/README
@@ -1 +1,3 @@
Web shop for shop.samanthony.xyz.
+
+Bùth is Gaelic for "shop".
diff --git a/cmd/webshopd/main.go b/cmd/buthd/main.go
index 519a299..60ab749 100644
--- a/cmd/webshopd/main.go
+++ b/cmd/buthd/main.go
@@ -1,3 +1,4 @@
+// Buthd is the API server.
package main
import (
diff --git a/doc/arch.dot b/doc/arch.dot
new file mode 100644
index 0000000..fa6f5aa
--- /dev/null
+++ b/doc/arch.dot
@@ -0,0 +1,18 @@
+graph {
+ graph [rankdir=BT; nodesep=1.0; ranksep=1.0]
+ node [shape=box]
+
+ client -- relay [label="HTTPS"]
+ client -- relay [label="9P/WS"]
+ relay -- shop [label="HTTP\nstatic html"]
+ relay --api [label="HTTP\nhtmx frags"]
+ relay -- api [label="9P/WS\napi"]
+ relay --auth [label="HTTP\nforms"]
+ api -- authfs [label="9P/TCP"]
+ auth --authfs [label="9P/TCP"]
+
+ subgraph cluster_lan {
+ relay, shop, api, auth
+ authfs [shape=cylinder]
+ }
+}
diff --git a/doc/arch.md b/doc/arch.md
new file mode 100644
index 0000000..faac44e
--- /dev/null
+++ b/doc/arch.md
@@ -0,0 +1,35 @@
+
+# Architecture
+
+Intended to be deployed on OpenBSD.
+
+- LAN
+ - shop.samanthony.xyz
+ - Basic web server, e.g. httpd(8)
+ - Serves static HTML files
+ - Serves scripts (js/wasm) including htmx.js
+ - api.shop.samanthony.xyz
+ - `buthapi` API server
+ - Serves htmx fragments
+ - Serves 9P {/cart, /checkout} to authenticated clients via websockets
+ - auth.shop.samanthony.xyz
+ - `buthauth` web authentication gateway
+ - Client-facing HTTP interface to authfs
+ - Handles registration and login forms
+ - authfs
+ - `buthauthfs` daemon
+ - Persistent user database
+ - Stores password hashes
+ - Manages client sessions
+ - Serves 9P to api and auth servers
+ - relay
+ - relayd(8)
+ - TLS proxy/gateway
+- WAN
+ - Client web browser
+ - HTML renderer, js/wasm interpreter
+ - Generates and stores its session ID (in a cookie)
+
+The LAN could be either a single OpenBSD host, several vmd(8) VMs, or several machines in a VPN, e.g. Tailscale.
+
+![[arch.png]]
diff --git a/doc/arch.png b/doc/arch.png
new file mode 100644
index 0000000..d9cb9f0
--- /dev/null
+++ b/doc/arch.png
Binary files differ
diff --git a/doc/auth.md b/doc/auth.md
new file mode 100644
index 0000000..a8aeb25
--- /dev/null
+++ b/doc/auth.md
@@ -0,0 +1,50 @@
+# Authentication
+
+## Registration flow
+This is how a new user registers himself in `authfs`, after which he can open sessions.
+- Client `GET`s `shop./register.html`
+- Client enters username and password, `POST`s form to `auth./register`
+- `buthauth` server registers user in `authfs`
+ - Creates `/users/<username>/`
+ - If it already exists, the username is taken; return error to client
+ - Writes password to `/users/<username>/passwd`
+- `authfs` ingests and hashes the password. Subsequent reads of `/users/<username>/passwdhash` will return the hash (`authfs` discards the cleartext password after it is hashed).
+- If successful, client can now login to obtain a session
+
+## Login flow
+This is how a client authenticates itself and creates a _session_. A session gives the client a shopping cart and access to the checkout.
+- Client `GET`s `shop./login.html`.
+- Client enters username and password (assume they already have an account).
+- Js or wasm script running in client generates a 256-bit session ID and injects it into the login form.
+- Client `POST`s the login form to `auth./login`
+- `buthauth` writes the password to `/users/<username>/login` in `authfs`
+ - If `/users/<username>/` doesn't exist, the username is invalid; fail the login
+- `buthauth` reads from `/users/<username>/login`.
+- If the password was correct, `authfs` returns a session ID corresponding to `/sessions/<id>/
+ - Otherwise if the password was wrong, `authfs` returns `Rerror`. `buthauth` fails the login, returning an error to the client
+- `buthauth` returns a session cookie, containing the session ID, to the client (with proper security params: HttpOnly etc.)
+- Client includes the session cookie in subsequent requests, giving it elevated privileges.
+
+On the client, session is a _session cookie_ (no expiry date). On the server, it should expire after `lastseen+t`. `authfs` periodically sweeps the sessions and removes old ones. `authfs` updates a sessions's last-seen time whenever it gets a request on `/sessions/<id>/*`.
+
+## API usage
+This is how a client accesses the 9P API once he's logged in (has a session cookie).
+- Client `GET`s `shop./foo.html`
+ - Includes cookie in request
+- Httpd returns static page and scripts
+- Script opens websocket on `api./ws`
+- Browser sends cookie automatically in upgrade request
+- `buthapi` validates session (using cookie) by checking if `/sessions/<id>/` exists on `authfs`.
+ - If not, the session is invalid, return 401
+- `buthapi` upgrades the websocket connection and serves 9P
+- Client uses ws/9p connection to access API
+ - E.g. pressing "add to cart" button does `Tcreate /cart/sku123`
+ - (will need to figure out how to wire this up with htmx/js and 9p js/wasm lib)
+
+## Logout flow
+This is how a client destroys an active session on the server.
+- Client clicks Logout button
+- Htmx `POST`s to `auth./logout`, includes the session cookie in the request
+- `buthauth` reads the session ID from the cookie
+- `buthauth` removes `/sessions/<id>` on `authfs`
+- `buthauth` returns a response header telling the client browser to destroy the session cookie.
diff --git a/doc/libs.md b/doc/libs.md
new file mode 100644
index 0000000..3934bf4
--- /dev/null
+++ b/doc/libs.md
@@ -0,0 +1,6 @@
+# Libraries
+
+https://github.com/acaloiaro/hugo-htmx-go-template
+https://github.com/docker-archive/go-p9p
+https://github.com/gorilla/websocket
+https://github.com/alexedwards/argon2id
diff --git a/doc/notation.md b/doc/notation.md
new file mode 100644
index 0000000..1b1303e
--- /dev/null
+++ b/doc/notation.md
@@ -0,0 +1,2 @@
+# Notation
+- `api./foo` is short for `api.shop.samanthony.xyz/foo` \ No newline at end of file