# UHHM, uhhm.no: the studio that builds portal and carries redoal. # Rebuilt from the live site (uhhm/questions) on 2026-09-23. The five # buckets the site already holds keep their live state and event # names (`events:`), so every record on the owners' desk stays where # it is. Two are new: a business trying the trainer on its own # workflow (`trials`, approved by a person before anything is built), # and what reaches UHHM as the custodian of the redoal network # (`custody_requests`, a desk of its own). business: UHHM language: en contact: { name: Bendik Aagaard Lynghaug, email: bl@uhhm.no } # Every mail the site sends comes from here (gdo on ergo delivers). mail: { from: "UHHM ", reply_to: bl@uhhm.no } about: > UHHM builds software around a clear argument: infrastructure, tools and products for organizations that can say what problem they have. It also builds and carries the redoal network - the gesture math, the relay, the first node, and the portal this site runs on - and answers for what that network keeps. groups: - { id: owners, label: Owners } # The people who answer for the redoal network: they host its first # node and relay, keep what is left there, and decide what stays. - { id: custodians, label: redoal custodians, invited_by: [owners] } # Owners onboard into every desk; a custodian can vouch for another. invites_by: [owners] # The front door opens modestly: one or two features, one role each, # and no button, before the offers further down the same page. The # two roles UHHM exists for: an organization with a problem, and the # people on the redoal network it keeps. does: - id: software for: commission text: > Software built around a clear argument. Infrastructure, tools and products for organizations that can say what problem they have - and a working draft of your own workflow as a site, before you pay for anything. - id: redoal for: custody text: > The redoal network, built and carried here: the gesture math, the relay, the first node, and the portal this site runs on. UHHM answers for what that network keeps. # Things to look at before asking for anything. shows: - id: built text: A few things already out in the world. resource: public: true source: { kind: gitea_starred, username: bl } jq: > .[] | {name: .name, description: .description, url: (if .website != null and .website != "" then .website elif .private then null else .html_url end)} - id: voices text: Live from the redoal relay - what has been left at places so far. resource: public: true source: { kind: url, url: https://relay.redoal.com/pulse } jq: > { "name": (.voices_echoed | tostring), "description": "voices echoed so far" } empty: The first voice has not echoed yet. needs: - id: commission who: an organization with a problem it wants software to solve wants: a considered reply to a brief, from people who read the whole thing lands_in: projects handled_by: owners stages: [open, accepted, declined] done_when: [accepted, declined] success: [accepted] events: { open: submitted } stages_text: { open: "received", accepted: "taken on", declined: "not a fit for us" } # The client plays too: they can withdraw the brief themselves. moves_by: { declined: [submitter, owners] } mails: { open: submitter, accepted: submitter, declined: submitter } max_fields: 4 collects: - { name: name } - { name: email, type: email } - { name: project, type: textarea } - { name: organization } - { name: org_contact, optional: true } # The client feature: the trainer tried on a real business. A person # approves each intake before anything is built from it. - id: trial who: someone who runs a business where requests, approvals and handovers get lost, and wants to see it as a site before committing to anything wants: a first draft of a site for their own roles, built from their own words and approved by a person first lands_in: trials handled_by: owners stages: [open, approved, previewed, declined] done_when: [previewed, declined] success: [previewed] stages_text: { open: "received", approved: "approved for a draft", previewed: "a preview is ready", declined: "not a fit for us" } mails: { open: submitter, approved: submitter, previewed: submitter, declined: submitter } attended_by: > approved -> previewed: the n8n workflow that builds the draft site from an approved intake (docker, rauthy, portal content) and sends the preview link. Until it exists, owners run portal-trainer by hand and press the button. max_fields: 6 collects: - { name: business } - { name: email, type: email } - { name: what_we_do, type: textarea } - { name: who_comes_to_us, type: textarea } - { name: what_we_keep_track_of, type: textarea } - { name: who_is_involved, type: textarea } - { name: what_slows_us_down, type: textarea, optional: true } - id: collaborate who: someone who wants to work with us on what we build wants: to be read as a person, and invited in if there is a fit lands_in: applicants handled_by: owners stages: [open, invited, active, departed, declined] done_when: [departed, declined] success: [departed] events: { open: applied, active: onboarded } # Declined before starting; after that a person departs. turned_down_from: [open, invited] stages_text: { open: "received", invited: "invited in", active: "working with us", departed: "moved on", declined: "not a fit for us" } max_fields: 3 collects: - { name: name } - { name: email, type: email } - { name: interest, type: textarea } - { name: note, type: textarea, optional: true } - id: subscribe who: someone who wants to hear from us when there is something to say wants: a note now and then, and an easy way out lands_in: subscribers handled_by: owners stages: [open, unsubscribed] done_when: [unsubscribed] events: { open: subscribed } stages_text: { open: "on the list", unsubscribed: "off the list" } moves_by: { unsubscribed: [submitter, owners] } max_fields: 2 collects: - { name: name } - { name: email, type: email } # This site proposes its own changes through itself. - id: propose who: a visitor who sees how a page on this site should change wants: the change read by a person and, if approved, made lands_in: development_proposals handled_by: owners stages: [open, approved, rejected] done_when: [approved, rejected] success: [approved] events: { open: proposed } stages_text: { open: "received", approved: "approved", rejected: "turned down" } max_fields: 3 collects: - { name: submitted_by } - { name: email, type: email, optional: true } - { name: page, optional: true } - { name: rationale, type: textarea } - { name: proposed_yaml, type: textarea } # The custodian role, on the front door: UHHM answers for the redoal # network it hosts. Owners see these too, and move nothing. - id: custody who: someone who runs, builds on, or is affected by the redoal network and needs the people who keep its first node wants: an answer from a custodian, and action where the network keeps something it should not lands_in: custody_requests handled_by: custodians watched_by: [owners] stages: [open, in_dialogue, resolved, declined] done_when: [resolved, declined] success: [resolved] stages_text: { open: "received", in_dialogue: "in dialogue with a custodian", resolved: "resolved", declined: "closed without action" } # The person affected says when it is resolved, not only the custodian. moves_by: { resolved: [submitter, custodians] } mails: { open: submitter, resolved: submitter, declined: submitter } max_fields: 4 collects: - { name: name } - { name: email, type: email } - { name: about, type: select, options: [Running a node of my own, Building on the network, Something the network keeps about me, Something else] } - { name: what, type: textarea } # Internal: a relationship worth tracking before it is a project. - id: prospect who: an owner who has met an organization worth following up wants: it written down and walked through its states from the desk enters_as: owners lands_in: organizations handled_by: owners stages: [prospect, client, past_client] done_when: [past_client] skippable: [client] stages_text: { prospect: "a prospect", client: "a client", past_client: "a past client" } events: { prospect: identified, client: became_client, past_client: churned } max_fields: 2 collects: - { name: name } - { name: contact } - { name: note, type: textarea, optional: true } # Held out from every model that writes words. The first eight are the # 2026-09-21 set (runs/uhhm-2026-09-21); the rest are for what is new. personas: - { id: architecture-firm, need: commission, text: "We run a small architecture firm and our project files are scattered across three cloud drives. We want to pay someone to set up proper storage and backups we control ourselves." } - { id: climbing-gym-founder, need: commission, text: "I have an idea for a booking app for climbing gyms and I'm looking for a team that can build the first version with me." } - { id: municipality-buyer, need: commission, text: "Our municipality needs an internal tool for tracking building permits. We are looking for a vendor who can scope it and deliver it." } - { id: exhibition-visitor, need: subscribe, text: "I saw your drawing network at an exhibition and liked it. I don't want to get involved, I'd just like to know when there is news." } - { id: tech-journalist, need: subscribe, text: "I write about Nordic technology. Keep me posted on what you release so I can cover it." } - { id: rust-developer, need: collaborate, text: "I'm a Rust developer between jobs and I like that you build in the open. I would love to contribute, and maybe join you." } - { id: interaction-designer, need: collaborate, text: "I'm an interaction designer. I want to work with you on the drawing interface." } - { id: student-intern, need: collaborate, text: "I'm a computer science student looking for a summer internship doing infrastructure work." } - { id: plumber-owner, need: trial, text: "I run a plumbing company with four vans. Jobs come in by phone and text and some get lost. I'd like to see how this would look for us before I spend any money." } - { id: dance-school, need: trial, text: "We are a dance school. Enrolments, cancellations and invoices all go through one email inbox that only I understand. Can you show me a version made for us?" } - { id: node-runner, need: custody, text: "I run a small server in Berlin and want to host a redoal node that peers with yours. Who do I talk to?" } - { id: recorded-name, need: custody, text: "Someone left a recording on redoal that says my full name and where I live. I want it gone, and I want to know who is responsible for that network." } - { id: app-builder, need: custody, text: "I'm building a music app and would like to use redoal's gesture addresses in it. Is that possible, and who decides?" } - { id: wording-fix, need: propose, text: "The subscribe page should say how often you actually write. I'd like to suggest that exact change to the page." } - { id: rewrite, need: propose, text: "One page on this site reads wrong to me and I know exactly how I'd rewrite it. Where do I send the new text?" }