[{"data":1,"prerenderedAt":98},["ShallowReactive",2],{"api:\u002Fapi\u002Fconfig:en":3,"api:\u002Fapi\u002Fnotes:en":8,"api:\u002Fapi\u002Fprojects\u002Fauth-service:en":32},{"email":4,"lang":5,"name":6,"role":7},"hello@ewsirdev.example","en","Ewsirdev","Web developer: websites, landing pages and Go services",[9,16,22,27],{"slug":10,"lang":5,"title":11,"date":12,"summary":13,"project":14,"minutes":15},"double-booking","Double booking: a transaction plus a partial UNIQUE index","2026-10-20","How booking-slots protects against two clients booking the same time at once, and how that protection is tested.","booking-slots",1,{"slug":17,"lang":5,"title":18,"date":12,"summary":19,"project":20,"minutes":21},"lease-and-fencing","Lease and fencing: how a queue neither loses a job nor lets a zombie finish it twice","A worker can crash, stall, or wake up after its job was taken by someone else. How the lease and the lease token in job-queue deal with that on SQLite.","job-queue",2,{"slug":23,"lang":5,"title":24,"date":12,"summary":25,"project":26,"minutes":21},"p95-nearest-rank","p95 in an uptime monitor: the nearest-rank method and what it does not hide","How uptime-monitor computes p50\u002Fp95 latency and uptime, why the nearest-rank method was chosen, and where those numbers can mislead.","uptime-monitor",{"slug":28,"lang":5,"title":29,"date":12,"summary":30,"project":31,"minutes":21},"websocket-from-scratch","WebSocket on the standard library: what really happens under new WebSocket()","Why realtime-rooms has its own RFC 6455 layer, which protocol violations it checks, and why a library is still the better choice for production.","realtime-rooms",{"slug":33,"title":34,"type":35,"stack":36,"description":52,"highlights":53,"run":58,"hue":59,"og":60,"shots":61,"case":69,"case_lang":5,"prev":92,"next":95},"auth-service","Keyring: an authentication service","backend",[37,38,39,40,41,42,43,44,45,46,47,48,49,50,51],"Go","stdlib crypto","SQLite","modernc.org\u002Fsqlite (no CGO)","JWT EdDSA\u002FEd25519","JWKS","PBKDF2","TOTP","RBAC","REST","OpenAPI 3","Vanilla JS","embed","Docker","GitHub Actions","An authentication and authorization service in Go: EdDSA JWTs with key rotation and JWKS, refresh tokens with rotation and theft detection, TOTP 2FA (RFC 6238) with backup codes, RBAC, lockout on brute force, password reset by a one-time token, an audit log, a device list, openapi.yaml. Not independently audited, not a production IdP.",[54,55,56,57],"EdDSA JWTs with key rotation and JWKS","Refresh tokens with rotation and reuse detection","TOTP per RFC 6238, checked against the RFC test vectors","An append-only audit log (database triggers)","cd auth-service && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fserver",20,"\u002Fog\u002Fen\u002Fauth-service.jpg",{"desktop":62,"desktop_sm":63,"mobile":64,"dw":65,"dh":66,"mw":67,"mh":68},"\u002Fshots\u002Fauth-service-desktop.webp","\u002Fshots\u002Fauth-service-desktop-sm.webp","\u002Fshots\u002Fauth-service-mobile.webp",1280,800,390,844,{"about":70,"features":73,"run":83,"notes":87},[71,72],"Keyring is an authentication and authorization service in Go: sign-up and sign-in, short-lived JWTs with key rotation and JWKS, refresh tokens with rotation and theft detection, TOTP 2FA with backup codes, RBAC, brute-force protection, password reset, an audit log and device management.","The standard library plus modernc.org\u002Fsqlite (no CGO); the interface is vanilla JS embedded through embed, and the API is described in openapi.yaml. This is a learning project: it has not been independently security audited and is not a production IdP; the limitations are listed below.",[74,75,76,77,78,79,80,81,82],"A JWT access token with alg=EdDSA (Ed25519): signing and parsing are written on the stdlib, none and HS256 are rejected before the signature check, and exp, nbf, iss, aud and typ are verified","Key rotation through the API and a public JWKS (RFC 7517\u002F8037): another service verifies tokens itself, and the old key lives on for access_ttl + 1 minute","Refresh tokens are stored as a SHA-256 hash; presenting an already used token revokes the whole token family and writes token.reuse_detected to the audit log","TOTP per RFC 6238 on the stdlib, checked against the RFC test vectors, a +-1 step window, replay protection and 8 one-time backup codes","Passwords: PBKDF2-HMAC-SHA256, 600,000 iterations, constant-time comparison, and a dummy hash for an unknown login so it does not reveal whether the user exists","Lockout on brute force with exponential delay (429 and Retry-After), plus a per-IP request limit","RBAC: permissions are read from the database on every request; you cannot leave the system without an administrator or disable yourself","Password reset by a one-time token that ends all sessions; an append-only audit log (database triggers forbid UPDATE and DELETE)","A device list with session revocation, a CSP without unsafe-inline, HttpOnly + SameSite=Strict cookies, a CSRF token and Origin checks",[84,85,86],"git clone \u003Crepository on request> auth-service","cd auth-service","SEED_DEMO=1 go run .\u002Fcmd\u002Fserver   # http:\u002F\u002Flocalhost:8101, the public demo accounts are shown on the sign-in page",[88,89,90,91],"Not independently audited and not a production IdP. There is no OAuth 2.0 or OIDC, WebAuthn, e-mail confirmation or social sign-in. Passwords use PBKDF2 rather than argon2id because argon2 is not in the stdlib.","TOTP secrets and private signing keys sit in the database in plain text, so a database dump lets someone forge tokens. The password reset e-mail is a stub (a log and a file), and there is no QR code for 2FA.","Instant revocation works only inside the service itself: a third-party JWT consumer learns of a revocation no earlier than exp (10 minutes). The rate limiter lives in the memory of one process, and there is no scaling out.","The demo passwords are public, shown in the interface and marked with a banner; they must not be used outside a demonstration. The -race tests run only in CI, and the Docker image has not been built.",{"slug":93,"title":94},"raft-kv","Raft KV: a key-value store on Raft",{"slug":96,"title":97},"finance-tracker","Ledger: personal finance tracker",1792697892726]