Latest Posts
No news found matching your search.
iBoard - ABOUT
iBoard is a little practicing project, figuring out how Elixir-development and AI comes together. So, I recently wrote this Blog-server. The content here doesn’t really matter! The project is not public (yet) but will be published at GitHub once it fulfills some basic quality standards ;-)
The iBoard Project This web application is written in Elixir with Phoenix, LiveView, TailwindCSS, DaisyUI, Ecto/Postgres, to name the most important.
ExDocs
The full dependency list reads like:
:bcrypt_elixir, "~> 3.0"
:phoenix, "~> 1.8.3"
:phoenix_ecto, "~> 4.5"
:ecto_sql, "~> 3.13"
:postgrex, ">= 0.0.0"
:phoenix_html, "~> 4.1"
:phoenix_live_reload, "~> 1.2", only: :dev
:phoenix_live_view, "~> 1.1.0"
:lazy_html, ">= 0.1.0", only: :test
:phoenix_live_dashboard, "~> 0.8.3"
:esbuild, "~> 0.10", runtime: Mix.env() == :dev
:tailwind, "~> 0.3", runtime: Mix.env() == :dev
:heroicons, github: "tailwindlabs/heroicons", tag: "v2.2.0"
:gen_smtp, "~> 1.2"
:swoosh, "~> 1.5"
:castore, "~> 1.0"
:req, "~> 0.5"
:telemetry_metrics, "~> 1.0"
:telemetry_poller, "~> 1.0"
:gettext, "~> 1.0"
:gettext_sigils, "~> 0.1.0"
:jason, "~> 1.2"
:dns_cluster, "~> 0.2.0"
:bandit, "~> 1.5"
:tzdata, "~> 1.1"
:boundary, "~> 0.10", runtime: false
:ex_doc, "~> 0.31", only: [:dev, :prod], runtime: false
:earmark, "~> 1.4"
With a big hug to this gorgeous community!
Follow the tag #iboard if you’re interested in the faith of this project.
Features so far (Buzzwords)
- Accounts, Users (phx.gen.auth)
- Posts, Drafts, Authors, Moderators, Likes, Followers
- User management, invite by e-mail
- Supports DaisyUI Themes
- Supports Locale and Timezone
Digitale Souveränität: Auch in Linz denkt jemand darüber nach
Die KPÖ Oberösterreich hat am 21. September eine Anfrage an den Linzer Bürgermeister veröffentlicht, die mich positiv überrascht hat. Gemeinderat Michael Roth-Schmida will von Dietmar Prammer wissen, wie es um die IT-Sicherheit und den Datenschutz im Magistrat Linz bestellt ist — konkret bei jenen Daten, die über Microsoft 365 verarbeitet werden.
Gefragt wird nach den Dingen, die man fragen muss:
- welche technischen und organisatorischen Schutzmaßnahmen es gibt (Verschlüsselung, Authentifizierung, Zugriffsüberwachung),
- wo die Daten tatsächlich liegen und ob sie die EU verlassen,
- wie das neue Netz- und Informationssystemsicherheitsgesetz (NISG 2026) umgesetzt wird,
- wie Sicherheitsvorfälle erkannt und gemeldet werden,
- und ob Open-Source-Alternativen wie openDesk oder Nextcloud überhaupt geprüft wurden.
Besonders der letzte Punkt freut mich. Die Diskussion um digitale Souveränität wird in Österreich meist geführt, als wäre sie ein Luxusthema für Nerds. Dabei geht es um etwas sehr Bodenständiges: Eine Stadtverwaltung verarbeitet die Daten ihrer Bürgerinnen und Bürger. Wenn diese Daten auf Infrastruktur liegen, deren Betreiber einer fremden Rechtsordnung unterliegt, dann ist das keine technische Detailfrage, sondern eine politische.
Andere haben das längst vorgemacht — Schleswig-Holstein steigt konsequent auf offene Software um, und auch in mehreren deutschen Kommunen läuft openDesk im Echtbetrieb. Es geht also. Es kostet Arbeit, und es kostet in der Übergangsphase Nerven, aber es ist keine theoretische Übung.
Ob die Anfrage eine brauchbare Antwort bekommt, steht auf einem anderen Blatt. Anfragen dieser Art werden gerne mit Verweisen auf bestehende Verträge und Zertifizierungen beantwortet. Trotzdem: Dass die Frage in einem Gemeinderat überhaupt gestellt wird, ist ein Fortschritt. Man muss die Fragen stellen, bevor man Antworten bekommt.
Ich bin gespannt, was aus der Sitzung am 24. September herauskommt.
Release 1.0.0
Version 1.0.0 is out. It collects a single day of work around one idea: this blog should be writable from the place where articles are actually written — a text editor and a terminal.
What is new
A JSON API. Six endpoints, authenticated with a bearer token, cover the whole life of an article: create it, upload its attachments, publish it, read it back, revise it. A key belongs to exactly one user and acts as that user, so nothing about visibility or ownership changes just because the request came from a script.
A client. post-article takes a Markdown file with a short front matter
block and does the rest: create the draft, upload the images, publish on
request, notify the followers only when asked. Drafts are the default,
because the only step that cannot be undone is the e-mail.
Key management in the browser. Admins mint and revoke keys under API Keys, with a guide on the page itself: where to store the key, which commands to run, what each endpoint does, and where the limits are. A new key is shown exactly once — only its hash is kept.
An audit trail. Every API call is recorded with method, path, status, IP address and the name of the key that made it. Rejected calls are recorded too. Whatever a script did, the audit log knows which key did it.
Downloads. The client and the agent skill can be fetched straight from the documentation, so a new machine needs nothing but a browser.
Smaller repairs along the way
Reading the first API-written article back from the site turned up three things worth fixing:
- The client split articles at the language separator even when that line sat inside a code example, cutting the text in half.
- The language switch on an article drew a border around the language you were not reading, which looked like the button to press.
- Relative timestamps only knew how to phrase the past, so a key valid for another year politely reported that it expired “just now”.
Try it
mix w_app_core.api_token create you@example.com --label laptop
post-article --check
post-article --publish article.md
This post was written in an editor and sent from a terminal.
Writing posts from the terminal
This blog now accepts articles over a JSON API. Write the text in your editor of choice, run one command, and the post lands here — as a draft first, so nothing goes out before you have read it in its final shape.
Why bother
The editor in the browser is fine for a quick note. It is less fine when the text already exists as a file, when the screenshots sit in a folder next to it, or when the article is the last step of a script that has just finished a release. For those cases the blog got an API.
The shape of it
Six endpoints, all authenticated with a bearer token:
GET /api/me the user behind the token
POST /api/posts create a post (draft by default)
GET /api/posts/:id read one of your own posts
PATCH /api/posts/:id revise a post, field by field
POST /api/posts/:id/attachments upload a file
POST /api/posts/:id/publish publish, optionally notify followers
A key belongs to exactly one user and acts as that user, so the same visibility and ownership rules apply as in the browser. Keys are stored hashed, expire after a year, and are minted and revoked by an admin under API Keys.
One command
The interesting part is the client. An article is a Markdown file with a small front matter block:
---
subject: The English headline
subject_de: Die deutsche Überschrift
tags: elixir phoenix
visibility: public
attachments:
- diagram.png
---
The English body. Embed the image with {diagram.png}.
--- de ---
Der deutsche Text, wieder mit {diagram.png}.
Then:
post-article --publish article.md
The script creates the draft, uploads the attachments, publishes, and prints
the URL. Leave off --publish and you get a draft to review. Add --notify
and the followers get the usual e-mail — with the images already in place,
which is exactly why uploading happens before publishing.
What it does not do
It does not unpublish, it does not delete, and it will not let you touch
someone else’s post. Those stay in the browser, where a mis-click is visible
and reversible. Revising works, though: a PATCH changes exactly the fields
it is given — this very paragraph arrived that way. Every API call is written
to the audit log with method, path, status, IP and the name of the key that
made it, so it is always clear which script did what.
This article was posted with the command above.
gitpro — a GitHub project board in the terminal
I read a project board far more often than I edit one. What is on the board is something I want to know while I am standing in the checkout, working — not after opening a browser, finding the organisation, clicking the right board and hunting down the card.
So: gitpro, version 1.0.0, now on Hex.
cd ~/Projects/some-repo
gitpro
That is the whole invocation. gitpro asks git which repository the working directory belongs to, asks GitHub which Projects v2 boards that repository is linked to, and opens the cards on one of them.
Typing is searching
There is no mode to enter and no key to press first. The search field has the keyboard from the moment gitpro opens — every printable character is the query’s, and everything else is bound to a key the field does not want. Every word has to match somewhere in the row: its number, title, repository, column, author, milestone, labels, assignees or state.
^F opens the switches: state (open, closed, merged, draft — only the
ones the board actually has), the board’s kanban columns, the labels the cards
wear and the people they are assigned to, each with an empty bucket of its own.
The list narrows behind the popup as the switches are set, so you can see what a
switch does before you commit to it.
^I shows a card in full — title, description, state, column, repository,
author, labels, assignees, milestone and dates. All of it arrived with the
board, so the popup opens instantly and keeps working with the network gone.
j and k walk to the next card without closing it: reading through a column
is opening the first card and holding j. ^O or Enter opens the card in a
browser.
How it is put together
The UI is Atui, a terminal toolkit in the
shape of Phoenix LiveView: views with state, a render/2 that returns cells,
and a runtime that owns the terminal.
It talks to GitHub through the gh CLI, which means
there is no HTTP client, no JSON dependency and no token to configure beyond
gh auth login.
Two things are deliberate. Failing happens in the terminal, not in the UI — “not a git repository”, “gh is not installed”, “this repository is not on a project board” are printed on stderr, with a non-zero exit status, before the alternate screen is opened. And everything that decides what is on screen is a pure function over data: the filters, the GraphQL decoders, the row widths, the window onto a list longer than the screen. So the interesting half of the application is tested without a terminal and without a token.
Installing
gh has to be installed and logged in with the project scope:
gh auth login
gh auth refresh -s project # if 'project' is not already in gh auth status
Then, in one step:
mix escript.install hex gitpro
The escript lands in ~/.mix/escripts, so that wants to be on your PATH.
Not yet
An MVP, and it shows. Next up: comments and sub-issues in the detail popup, moving a card between columns from here rather than in a browser, and remembering the switches per board between runs.
- Hex: https://hex.pm/packages/gitpro
- GitHub: https://github.com/iboard/gitpro
- Docs: https://hexdocs.pm/gitpro
GPL-3.0-or-later.