API-key surface for live call events from a customer PBX
Companion to VoipRecordingUploadController, on a deliberately separate VOIP_CALL_EVENTS scope: uploading a recording after the fact and making somebody's phone ...
Let an on-premise PBX report a ringing extension
RingCentral and JustCall publish INBOUND_RINGING from their webhooks. FreePBX and KamoPBX publish nothing -- they are the customer's own Asterisk and nothing wa...
Ring a member's phone for an incoming call
VoipStompRelayController relayed voip.call.* ringing events to the browser and deliberately never pushed them, reserving incoming-call alerting for 'the future ...
Send to AI opens the terminal it runs in, and the tabs get icons
A dispatch now comes back naming a terminal, and the console opens it. That is not a courtesy view of something already underway — the hand-off is PREPARED on t...
Pin the session id, and link the terminal session into VS Code
`--session-id <uuid>` is chosen at prepare time rather than left to the CLI, which is what makes the editor hand-off deterministic: the id is known before the s...
Apply in one request, and say why a role cannot be applied to
Follow-up to a review of my own commit. Six real findings. APPLYING IS NOW ONE REQUEST. The résumé used to upload after the application was created, and a fail...
A REQUIRED resume is now actually enforced
A review of my own commit found the enforcement it claimed. JobApplicationService carried the comment "a REQUIRED resume is enforced at that point by requireRes...
Send to AI now runs in a Remote Terminal the operator can watch
The dispatch prepares the hand-off on the dev machine — screenshots, video and the composed prompt written to disk — and answers with the terminal it will run i...
A dispatch names the terminal it is running in
Send to AI is hosted in a Remote Terminal now, so the answer has to say which one — the console opens that window, and opening it is what starts the session. B...
Host a Send-to-AI dispatch in a Remote Terminal
composed prompt — and answers with an opaque TOKEN. A terminal that connects command typed into it. THE TOKEN IS THE WHOLE SECURITY DESIGN. `bootstrap=<command...
The navigator had no surface of its own, so it was transparent
ChatNavigatorPanel draws no background — it is a positioned, animated shell, and the column inside it is what makes it opaque. ChatSwitcherColumn supplies `bg-w...
Jobs Available on /hr, and a Careers tab for every member
Two surfaces over one feature. /hr/careers is where HR writes the adverts and reads who applied; **************** is the internal board an employee browses and ...
Scan hr.careers, and drop the frozen work-location CHECK
Two changes, both required before the careers entities can reach the database. The @EnableJpaRepositories list is an explicit array while @EntityScan is wildca...
Seed the two careers rights onto HR's existing authoring group
MANAGE_JOB_POSTINGS and VIEW_JOB_LISTINGS are inserted as NOT_SPECIFIED like every new right, so without a seed the /hr/careers tab and the member Careers tab w...
/api/docs/careers — job postings, the member board, and applying
Two controllers behind one feature, and the split is the point. /careers/** is HR's and requires MANAGE_JOB_POSTINGS; /careers/member/** is the employee's and r...
Job postings and applications — the schema behind /hr/careers
Two entities, six enums and two repositories under com.kamo.z.shared.hr.careers, plus the three shared types the feature had to widen. JobPosting is three grou...
The terminal list was always empty — tmux escapes control chars
The field separator was a unit separator (0x1f), which is the obvious choice and silently wrong: tmux OCTAL-ESCAPES control characters in `-F` format output, so...
A list of your live shells, in the panel and beside the window
Terminals joins the Interaction Center to the right of Sales Scripts, gated on a provisioned dev machine — the same gate as the Linux Desktop nav option, read o...
Composed mail broke lines mid-word, for every recipient
Quill 2's getSemanticHTML() -- the value react-quill-new hands to onChange, and so every byte of body a member types -- ends its text conversion with .replaceAl...
A "why can't I accept?" override on the filter bar
Right-aligned on the /leads filter bar, wearing the Leads nav icon with the same prohibited emblem — so the thing on the nav that says "you are blocked" and the...
List, rename and end a member's terminals
GET **************** DELETE and PATCH on /terminals/{id}. Every one of them resolves the Linux account from the CALLER'S OWN SESSION and passes no account name...
Clip the background carousel so its zoom-in stops flashing a scrollbar
The incoming frame animates in from scale 1.04, and its wrapper did no clipping, so the overscale hung ~2% of the viewport past the bottom edge. A transform ove...
List, rename and end a member's terminals
GET /terminals?user=X, POST /terminals/<id>/kill, POST /terminals/<id>/title. THERE IS NO DATABASE BEHIND THIS, and that is the design rather than a shortcut. ...
A password shortcut in the hero, and a narrower gate
Two halves of the same subject. The shortcut. Changing your password was three rooms deep behind the rail — the one thing the room layout made genuinely harder...
Only the member or god may set a member's password
MANAGE_MEMBER_SECURITY passed the change-password gate, which made "can administer members" and "can take over any member's account" the same right. They are no...
Every terminal handshake was 403 behind the edge proxy
The reported symptom was a terminal that opened and immediately said "Disconnected", with nothing to act on. Reproduced end to end: the ticket mints fine (200) ...
Assert the mask obfuscates, and skip the gate for callers with no leads
LeadFieldMaskTest still expected a null e-mail. The mask now obfuscates rather than blanks — a blank field reads as "this lead has no e-mail", which is a differ...
Wire the accept-gate mock into the FFA spend test
acceptNextLead consults LeadAcceptEligibilityService before it reaches any of the credit logic these cases are about, and the test builds the service by reflect...
A new terminal could reattach to an old shell after a refresh
The shell was keyed on the tool window's id, and @kamo/tool-core builds those as `${toolId}-${++idCounter}` from a counter that RESETS on every page load. So th...
Prohibited nav emblem, Assign to Myself, and obfuscated contact masking
**The Leads nav icon says when you cannot accept.** A member who holds leads they have not worked gets a red block emblem over the Leads icon and a tooltip nami...
Serve the widened accept gate, add self-assignment, and close two contact-mask bypasses
**Endpoints for the new gate.** /leads/available-summary and /leads/available now report both reasons a member is blocked — leads owed a note since they were as...
Widen the accept gate, obfuscate masked contact info, let free-for-all leads be claimed
Three related changes to the lead-assignment core. **The accept gate now asks two questions, over every assigned lead.** LeadAcceptEligibilityService replaces ...
Flags on languages, and stop copying platform lists
Completes c20bac57. Languages now carry their flag, the same one the NavTop switcher shows and from the same source — the closed value as well as every row in ...
Record what the Remote Terminal deploy actually taught
Three defects, all the same shape: present in the tree, absent from the thing that serves requests — an IngressRoute in a directory nothing applies, a client pa...
The reaper used `su` too — runuser, and no shell anywhere
Dropping su from the exec path left it in reap_idle, which runs on every terminal connect. Same three objections, and the last one is the one that bites: `su - ...
The ticket request had no route behind it
The window fetched **************** and nothing served it. There is no catch-all under /api/security — every area is proxied by a route file of its own, and the...
Move the terminal IngressRoute to where something applies it
This repo's workflow applies exactly configmap.yaml, deployment.yaml and service.yaml, so a fourth manifest beside them was never applied by anything — the rout...
The terminal's IngressRoute was in a directory nothing applies
It was written into securityservice/k8s/ beside the deployment, which reads as the obvious home and is not one: that repo's workflow applies exactly configmap.y...
Drop `su` — it swallowed SIGWINCH, so no terminal ever resized
Caught by running the protocol against the live machine, not by reading it: after a resize control frame, `tput cols` in the shell still reported the size the t...
A custom-rendered field can be edited again
Completes c20bac57. Field's `renderValue` was returning in place of the whole read view, which included the button that opens the editor. So any field that ren...
Rebuild My Profile as a hero and five rooms
**************** was one 1,999-line component rendering thirteen identical cards down a single column. One SectionCard helper gave all of them the same radius, ...
A refused terminal handshake answered 200
Found by probing the deployed endpoint, not by reading it. Returning false from a HandshakeInterceptor aborts the handshake but sets nothing on the response, so...
Rebuild — the last run died fetching actions/checkout
Run 5861 failed in two seconds, before any compilation: the runner could not reach data.forgejo.org to fetch actions/checkout. Nothing to do with the tree, and ...
A real Linux terminal in a tool window, and a submenu to reach it
The Linux Desktop nav option becomes a menu instead of a launch — Remote Desktop with its sizes, Remote Terminal — exactly as the softphone owns call, voicemail...
State why the 101 is read before anything is written
The agent parses the request with a buffered reader and then pumps the raw socket, so a byte sent ahead of the status line would be stranded in that buffer and ...
Pin the no-pipelining invariant on the PTY upgrade
self.rfile is a BUFFERED reader over the socket and pump reads the RAW socket, so a caller that sent bytes behind its request line would strand them in that buf...
Drive a real PTY and a real socket through the terminal pump
The existing tests pin argv and framing in isolation; these four prove the pieces actually move bytes. A shell is forked under a genuine pty.fork and pumped ove...
A fourth window state — float, drag to detach, drag to reorder
A popup tool window has had three states: docked in the row along the bottom, maximized over the viewport, or minimized to a chat head. This adds FLOATING — det...
Serve Remote Terminal — a real shell over a WebSocket
Bridges a browser to a PTY on the member's own dev machine. Binary frames are the terminal, text frames are control; on the far hop an HTTP 101 onto a length-pr...
Give the agent a PTY endpoint for Remote Terminal
GET /pty upgrades the connection to `kamo-pty` and becomes a shell: one PTY per browser tool window, `su - <user>` into `tmux new-session -A`, frames pumped bot...
Like what you see shipping?
All of it arrives in your workspace on its own. Start on the free plan and read this page again in a month.
