August 2026
LinkedIn Publisher
A LinkedIn integration inside log.exe: a registered developer app, a full 3-legged OAuth flow, and publishing to my own feed through the UGC Posts API. Captions are built from real project rows in D1 — title, tagline, highlights, tech stack, media — so a post can only claim things the CMS already knows. The interesting part was working out what the API will not give me, and saying so in the UI instead of faking it.

Draft a post from project facts, then publish straight to my own LinkedIn feed — without pretending I have analytics I cannot reach.
Draft a post from project facts, then publish straight to my own LinkedIn feed — without pretending I have analytics I cannot reach.
What it is
A LinkedIn integration built into log.exe. It does two things: it turns a project row in D1 into a paste-ready caption, and it publishes that caption to my own feed.
The generation side was the easy half. The honest half was harder.
The OAuth part
LinkedIn's developer app gives you a narrow, well-defined set of grants. I registered one and wired the standard 3-legged flow:
openid,profile,emailfor identityw_member_socialfor posting
The OpenID sub is what matters — it becomes urn:li:person:{sub}, the author URN the UGC Posts API wants. Publishing goes out with X-Restli-Protocol-Version: 2.0.0, and a successful post stamps lastPostedAt, which is the single fact the comeback coach computes its whole gap analysis from.
Tokens last 60 days. This app has no programmatic refresh, so rather than let it break quietly two months from now, the UI shows the expiry date and tells me to expect a reconnect.
What the API will not give me
This is the part I want to keep visible.
I cannot read my own analytics. Profile views and post impressions need r_member_profileAnalytics and r_member_postAnalytics, which ride on the Community Management API. On this app, the Request access button is greyed out — it is not a matter of waiting for approval, I cannot even apply.
So every number in this feature is one I pasted in by hand from LinkedIn's own UI, and the page says exactly that where the numbers appear. The suggested posting windows are labelled Starting guess (MYT) … Not from your analytics, because they are a fixed rule of thumb and nothing else.
It would have been easy to render those windows next to a "connected" badge and let them read as insight. They are not insight.
Facts, not fluency
Captions are assembled from what the CMS already knows: title, tagline, description, highlights, tech stack, attached media. If the project has ten highlights, the post can draw on them; if it has none, the template says less rather than inventing more.
Two rules earn their place:
- A private repo's link never ships.
quack-quackis private, so agithub.com/…/quack-quackline 404s for every reader. The template used to re-emit it on every regenerate, silently undoing a fix I had made by hand. Now the link only appears when the project is public. - The model cannot write the clock. NIM may reword tone, plans and hook ideas. It may not produce posting times — a rewritten
Wed–Fri 15:00–20:00is a number with no source, which is precisely what that same block warns against.
Voice as data
I scraped my own LinkedIn posts and wrote down what I actually sound like: simple English, first-person, build logs rather than product blurbs, Melayu reserved for community posts, 150–280 words, 3–4 niche hashtags.
That lives in docs/linkedin-voice.md and is compiled into the template — not appended to a prompt and hoped for. The template hits the shape reliably. The optional NIM pass is what gets closer to the feel, and a badge always tells me which one I am reading.
More projects
view all →
Mukawa AR Trail
A web AR trail built as a study for the Mukawa dinosaur story. Eight printed boards hang around a building, each with a picture and a QR. The QR only opens a link, so the phone's own camera does the scanning; the app asks for the camera when the visitor taps, recognises the picture itself as an image target, and stands the creature on the board as you move. Every board found unlocks its creature in a collection saved on the phone, with no sign-up. Built end to end by me: the trail app, the image targets, the printed panels and the deploy.
Kaeru Plugins
Two independent Claude Code plugins from one marketplace: kaeru lets non-technical staff safely edit a website's text, images, and colors in their own language and submit a PR for review — git stays hidden, it never merges; octo is the developer-facing GitHub + CI/CD toolkit — repo bootstrap, the issue → PR → merge flow, CI checks, and releases. The name folds in three meanings at once: 変える (change/edit), 帰る (return — a PR coming back for review), and 蛙 (frog — the mascot) — chosen so non-technical staff hear "Kaeru shite!" as friendly, not technical.

API Scalar
Mini-project inside log.exe: interactive OpenAPI docs powered by Scalar. Same repo as the parent — not a separate product. API skala, skalarrrrr, skala.