Alone in the Basement
The road from a static website and a few server scripts to a live support and product platform I own end-to-end.

Contents
Overview#
Ethica did not start as a platform.
It started as a handful of practical problems.
A server that needed to run something.
A script that solved one annoying task.
A static site that explained what I was building.
A status page.
A documentation portal.
A ticket system.
A small admin panel.
One more table in Postgres.
One more route in server.py.
And somewhere along the way, the pieces stopped being separate experiments.
They became a stack.
Not a huge company stack. Not a polished enterprise product with a team behind every feature. Just one person, sitting in a basement, trying to build infrastructure that can be understood, operated, moved, repaired, and owned.
That is where Ethica comes from.
Key Points#
- Ethica started as small self-hosted tools and grew into a real platform.
- The platform now has live admin, CRM, ITSM, newsletter, monitoring, customer portal, audit log, and documentation access.
- The public Hugo marketing site is being migrated into the same Python/Postgres application.
- The goal is not vendor lock-in, but self-owned infrastructure with support and consulting available.
- The work has been built alone, but not randomly: documentation, source-of-truth pages, patch notes, small commits, and smoke tests matter.
Setup#
There was no grand setup in the beginning.
The first version was just practical: get something online, make it run, document what exists, and keep improving it.
The public site started as Hugo. That made sense at the time. It was fast, clean, easy to write in Markdown, and simple to deploy. It gave Ethica a public face before the platform around it had fully formed.
But the operational side kept growing.
The admin was live. The CRM was live. The ticket system was live. The newsletter backend was live. The status page was live. The project board was live. The user roles, audit log, customer portal, and documentation access rules were live.
At some point, the static marketing site became the odd part of the system.
The platform had moved into Python and Postgres, while the public content still needed a build step, a container rebuild, and a dependency chain around Hugo and Docker images.
That is the next transition: moving Ethica.no from a static marketing layer into the same live application that already runs the operational side.
Publishing should be a database write, not a deploy.
Prerequisites#
The real prerequisites were not just software packages. They were habits:
- A willingness to own the whole stack.
- Enough documentation to understand the system later.
- A database-first mindset for operational data.
- Small, reversible changes.
- Smoke tests after risky work.
- Clear role boundaries.
- A refusal to solve every problem by adding another hosted third-party service.
Configuration#
Ethica is becoming one Python/Postgres platform for public content, support, newsletter, tickets, products, projects, status, and admin.
Not seven disconnected tools. Not a static site next to an app. Not a marketing shell over a hidden system.
One platform.
Active Today#
The current Ethica platform already includes working internal and customer-facing systems:
- Admin portal with role-based access.
- CRM with companies, contacts, tags, pipeline, and project views.
- ITSM/service desk with tickets, comments, assignments, teams, customer replies, internal notes, and attachments.
- Customer portal for account access and company-scoped tickets.
- Company admin support, where selected customer users can manage company users and tickets.
- Public status page with grouped systems, maintenance state, and status notes.
- Monitoring and alert handling for deployments, alarms, SSL checks, and incident ticket creation.
- Newsletter backend.
- Audit log for sign-ins, failed logins, password changes, and customer ticket activity.
- Documentation access model with reader accounts and per-folder grants.
- Existing Hugo-based public marketing site.
- Dynamic homepage parts already powered by the database.
It is not perfect. But it works. And more importantly: it is understandable.
The tables are known. The routes are known. The access rules are known. The failure modes are becoming known.
That matters.
In the Pipeline#
The next step is not to build a second platform next to the first one.
The next step is to move the remaining static parts into the platform that already exists.
Planned work includes:
- Moving blog posts and legal pages from Hugo into a live Postgres-backed CMS.
- Rendering public pages through
server.pyusing the same visual design. - Adding live RSS, sitemap, and search indexing from database content.
- Creating a public support entry point backed by the existing ticket system.
- Adding a newsletter archive using the existing newsletter backend.
- Building a project portfolio from the existing project data.
- Moving product and marketing pages into the live app.
- Removing the Hugo build step once all public routes are migrated.
- Keeping the current look while replacing the static generation layer underneath.
The goal is simple:
One Python/Postgres platform for content, support, newsletter, tickets, products, projects, status, and admin.Docker Compose#
There is no single docker-compose.yml that explains the whole journey.
Ethica is not one container pretending to be a platform. It is a stack of services, routes, tables, jobs, and boundaries that have grown around real operational needs.
The important pattern is this:
# Conceptual pattern only
services:
app:
image: ethica/app
depends_on:
- postgres
environment:
- DATABASE_URL=postgres://ethica:ethica@postgres:5432/ethica
postgres:
image: postgres:17
volumes:
- postgres_data:/var/lib/postgresql/data
proxy:
image: traefik:v3.3
ports:
- "80:80"
- "443:443"
volumes:
postgres_data:The exact services can change. The principle stays the same:
Own the runtime. Own the data. Own the routes. Own the rollback path.
Code Examples#
The work has increasingly followed one rule:
Build it so you can explain it later.That means small helpers, clear routes, and comments that explain why a block exists.
Example migration comment style:
# Live CMS route: takes precedence over Hugo static output for migrated posts.
# This lets Phase 1 serve DB content while the rest of the site still falls
# through to the existing public/ directory.Example public visibility rule:
def is_public_content(row, now):
return (
row["status"] == "published"
and row["published_at"] > 0
and row["published_at"] <= now
)Drafts should never become public by accident.
Tickets should never leak across companies.
Internal notes should never render for customers.
Documentation grants should never become decoration only.
Those rules matter more than clever code.
Images and Screenshots#
This post can work well with one calm hero image rather than a technical screenshot.
Suggested image ideas:
- A dark basement desk with a terminal open.
- A server rack or homelab machine in low light.
- A dashboard screen with status cards and logs.
- A symbolic image of a single workstation controlling multiple systems.
Where to store images:
- Create a folder for the post:
content/posts/alone-in-the-basement/ - Place images in the same folder:
content/posts/alone-in-the-basement/feature.png - Reference images with relative paths:
Feature image note:
If this post uses feature.png, copy a valid PNG file into the post folder root.
Do not generate or write a placeholder image file with scripts. Invalid PNGs can break the Hugo build.
Troubleshooting#
Building a self-owned platform alone creates a different kind of troubleshooting list.
The hardest issues are not always technical errors. Sometimes they are boundary errors.
- When a route leaks something it should not, I need to understand why.
- When a deploy fails, I need to read the logs.
- When a role has too much access, I need to fix the rule.
- When a customer ticket should be visible to one company and invisible to another, I need to know exactly where that boundary is enforced.
- When Hugo, Docker images, Postgres, proxy rules, or CSS start fighting each other, I need a rollback path.
That is why documentation, patch notes, source-of-truth pages, smoke tests, and small commits have become part of the platform.
Not because it sounds professional.
Because without that discipline, a self-owned platform quickly becomes a pile of clever code nobody trusts.
Best Practices#
The most important lessons so far are simple:
- Build in phases.
- Keep changes reversible.
- Smoke test after each risky step.
- Keep the live site working.
- Do not duplicate systems that already work.
- Use the existing source of truth.
- Keep role checks server-side.
- Document why things changed, not only what changed.
- Prefer boring operational clarity over clever abstractions.
- Make the stack possible to explain later.
Built alone does not mean built randomly.
I have used agents, tools, and AI while building this. I am not trying to pretend otherwise.
But the responsibility is still mine.
The direction, the testing, the boundaries, the security decisions, the rollback paths, and the final judgement all land here.
In the basement.
Open Infrastructure, Not Vendor Lock-in#
Another important idea behind Ethica is ownership.
I like open code. I like MIT-first components. I like software that can be read, modified, moved, replaced, and operated without asking permission from a vendor.
That does not mean pretending every part of the world is built from scratch. It means being deliberate about dependencies and licenses.
Where MIT fits, use MIT. Where selected AGPL-compatible parts make sense, understand why they are there.
The point is not purity.
The point is control.
Ethica should not become a lock-in trap. It should not depend on a hidden hosted control plane, a closed admin surface, or a licensing model that makes the customer afraid to leave.
Support, consulting, hosting guidance, and implementation help can still be offered.
But the customer should not need to be trapped for that support to have value.
That difference matters.
The Basement as a Control Room#
There is something strange about building this kind of system alone.
On the screen, it starts to look like a real platform. There is a logo, an admin panel, tickets, projects, monitoring, status pages, customers, roles, documentation, and product text.
But behind it is one person in a basement, trying to make all the parts fit together.
That can feel too small.
Too small a team. Too small a budget. Too little process. Too little enterprise polish.
But it also makes the work honest.
Every feature exists because there was a real need for it. Every shortcut eventually becomes visible. Every security rule has to make sense. Every page has to earn its place.
Ethica is being built one piece at a time:
- one route
- one table
- one smoke test
- one bug
- one migration
- one commit
- one lesson
References#
This post is not based on external references. It is a snapshot of the road so far.
Useful internal references:
content/docs/source-of-truth.md- dated patch notes
- CRM/ITSM admin reference
- Hugo-to-CMS migration plan
- authentication and audit documentation
- smoke test reports
Closing#
Ethica is no longer just a website.
It is not only a CRM. It is not only a ticket system. It is not only a documentation portal. It is not only a status page.
It is becoming a small, self-owned support and product platform built around real operational needs.
The direction is clear:
- live content
- customer support
- ticket handling
- CRM
- project delivery
- monitoring
- public status
- newsletter
- documentation access
- auditability
- self-hosted infrastructure
- open foundations
Some platforms begin in boardrooms.
This one began in a basement.
With a server, a terminal, a lot of broken things, and a stubborn belief that owning the stack is still worth it.
That is the road so far.
And I am still building.