Laraue Blog — .NET, Nuxt, Telegram Bots and AI Workflow

Real code, real decisions, real tradeoffs. We write about what we build — .NET libraries, Telegram bots, AI integrations, and the architecture mistakes worth learning from.

DeepSeek API in .NET — an OpenAI-compatible client without an SDK, Ollama for development and AI that tidies up notes
Part 26 of the series about building a Telegram task tracker in a tiny team. How Boards got AI that turns a stream of thoughts from a chat into a neat task: DeepSeek in production, Ollama for local development, our own OpenAI-compatible client on HttpClient and the /aisave command in Telegram.
Read →
A Telegram bot for group chats in .NET — linking a chat to a board and tasks from messages
Part 25 of the series about building a Telegram task tracker in a tiny team. How the Boards bot learned to work in group chats: linking a chat to a board with /link, two message save modes, the admin check, privacy mode and a group turning into a supergroup.
Read →
JWT in an HttpOnly cookie instead of localStorage — cookie authentication in ASP.NET Core and Nuxt
Part 24 of the series about building a Telegram task tracker in a tiny team. How the web app token moved from localStorage to an HttpOnly cookie: how ASP.NET Core reads a JWT from a cookie, what the Nuxt frontend needed for it, how to set up CORS, and how we made the project easier to run locally.
Read →
Task comments — permissions, background loading and API pagination without COUNT
Part 22 of the series about building a Telegram task tracker in a tiny team. How task comments work in Boards: who can read, write and edit them, why they load in a separate background request, and how pagination works without a COUNT query.
Read →
Telegram as file storage for a web app — uploads through a bot and one attachment model
Part 21 of the series about building a Telegram task tracker in a tiny team. How we let the web app upload images to tasks and store them in Telegram through a bot and brought files from the bot and from the web under one attachment model.
Read →
Task change history — how we built an audit log in a task tracker
Part 20 of the series about building a Telegram task tracker in a tiny team. How Boards got a change history for tasks, why we write it explicitly in services in the same transaction as the changes themselves, and why EF Core interceptors, temporal tables and event sourcing did not fit.
Read →
Custom fields for tasks — EAV vs JSONB in PostgreSQL, a separate table per type and filtering by values
Part 19 of the series about building a Telegram task tracker in a tiny team. How we added attributes (custom fields) to issues, why we chose an EAV model with a separate value table per type instead of a JSONB column, and how filtering tasks by attribute values works.
Read →
Sequential issue numbers — a counter on PostgreSQL INSERT ON CONFLICT, space keys and organization URLs
Part 18 of the series about building a Telegram task tracker in a tiny team. How issues got sequential numbers like BRD-42, why a counter on INSERT ... ON CONFLICT DO UPDATE suited them better than a PostgreSQL sequence, and how space keys and organization URLs work.
Read →
Access control in a task tracker without roles — an alternative to RBAC, four versions of the model and space-level permissions
Part 17 of the series about building a Telegram task tracker in a tiny team. Why Boards has no roles, how the permission system went through four versions looking for a balance between flexibility and simplicity, and why permissions are now granted only on the organization and the space.
Read →
Multi-tenancy in a SaaS task tracker — organizations, a personal organization for every user and a separate organization JWT
Part 16 of the series about building a Telegram task tracker in a tiny team. How we added organizations to Boards: a shared-database data model, a personal organization for every user, joining by an invite code, and a separate JWT issued when a user enters an organization.
Read →
API integration testing in ASP.NET Core — WebApplicationFactory, a real PostgreSQL and a typed controller proxy
Part 15 of the series about building a Telegram task tracker in a tiny team. Why we wrote our first integration tests when the app was already in production, how the test host on WebApplicationFactory with a real database works, and our own proxy that turns a controller method call into an HTTP request.
Read →
Spaces in a task tracker — grouping boards by project, the data model and moving existing users' data
Part 14 of the series about building a Telegram task tracker in a tiny team. How spaces group boards by project, why the space lives on the board and not on the issue, and the SQL migration that gave every existing user a default space.
Read →
We built a markdown CMS backend in .NET, then dropped it for SSR in Nuxt
Why we deleted our own markdown-to-API backend and moved the blog into the Nuxt app — the frontend/backend sync problem, the things the system did not support, and what got simpler after the move.
Read →
How we run our backlog in Boards with Claude Code over MCP
Part 27 of the series about building a Telegram task tracker in a tiny team. We built an MCP server for Laraue Boards to understand how MCP works, then started using it with Claude Code every day. How the first version came together in two hours, what was wrong with it, and the daily loop it made possible.
Read →
Telegram Login Widget vs Mini App auth in .NET — two validation schemes, one JWT
Part 13 of building a Telegram task tracker in a tiny team. How to add Telegram Login Widget authentication to a .NET and Nuxt web app next to the Mini App's: linking the domain in BotFather, validating the widget data (a different secret from init data), rejecting outdated data by auth_date, and issuing the same JWT.
Read →
One card from a Telegram album — handling media groups and edits in a bot, without timeout hacks
Part 12 of building a Telegram task tracker in a tiny team. Telegram delivers an album as a burst of separate messages and an edit as a fresh update — here is how to turn a media group into a single record without the usual timeout accumulator, and how to treat an edit as an update instead of a duplicate.
Read →