Home Inventory Management Application for managing your stuff and home(s).
  • Go 82.2%
  • templ 17.2%
  • CSS 0.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-11 15:14:24 +02:00
.builds build: Split CI into branch and tag manifests 2026-08-13 14:37:19 +02:00
cmd/hima feat: A home's own currency and exchange rates 2026-09-11 13:55:00 +02:00
docs/design docs: Design households, several families on one server 2026-09-11 15:14:24 +02:00
internal feat: This home, storage and About 2026-09-11 14:16:41 +02:00
pkg feat: This home, storage and About 2026-09-11 14:16:41 +02:00
scripts build: Add sourcehut manifest and a smoke test 2026-08-12 15:01:40 +02:00
.air.toml dev: Make dev run 2026-08-18 15:52:31 +02:00
.gitattributes build: Hide generated and vendored files from diff 2026-08-18 16:06:50 +02:00
.gitignore build: Ignore the build task's output directory 2026-08-18 16:06:50 +02:00
.golangci.toml build: Finish moving the module path 2026-09-11 13:34:33 +02:00
.tokeignore feat: Use shadcn-templ 2026-08-18 16:06:50 +02:00
CLAUDE.md feat: A home's own currency and exchange rates 2026-09-11 13:55:00 +02:00
components.json build: Finish moving the module path 2026-09-11 13:34:33 +02:00
CONTRIBUTING.md build: Move module path to src.timharek.no/tim/hima 2026-09-09 21:53:48 +02:00
go.mod build: Move module path to src.timharek.no/tim/hima 2026-09-09 21:53:48 +02:00
go.sum Use tint for colored slog output in serve 2026-08-28 09:48:14 +02:00
go.tool.mod build: Move module path to src.timharek.no/tim/hima 2026-09-09 21:53:48 +02:00
go.tool.sum feat: Use shadcn-templ 2026-08-18 16:06:50 +02:00
hima.example.toml feat: A home's own currency and exchange rates 2026-09-11 13:55:00 +02:00
hk.pkl build: Add mise, hk ++ 2026-08-11 21:39:06 +02:00
LICENSE docs: Add readme, contributing guide and license 2026-08-12 15:01:34 +02:00
mise.toml feat: A home's own currency and exchange rates 2026-09-11 13:55:00 +02:00
README.md build: Move module path to src.timharek.no/tim/hima 2026-09-09 21:53:48 +02:00

Hima

A CLI for your home inventory: what you own, what you paid, and the receipts that prove it. Hima means "home" in a Norwegian dialect.

It is one binary and one SQLite database. Your receipts stay the files they already are, in a directory you point hima at; the database holds what they mean. Nothing is uploaded anywhere, and nothing needs a server.

hima inbox                  # what is waiting
hima inbox triage           # work through it, in $EDITOR
hima items triage --gaps value
hima items list in:loftet tag:verktøy
hima export items --format pdf -o inventory.pdf

Pre-1.0. The command surface is settling but not settled, and the schema still changes between tags. Migrations run on open and are meant to be safe; back up before upgrading anyway.

Installing

go install src.timharek.no/tim/hima/cmd/hima@latest

Release tarballs, with checksums, are attached to each tag on the repository.

Configuring

hima runs with no config file at all — one home, a database under $XDG_CONFIG_HOME/hima, receipts in ~/Documents/receipts. That is enough to try it and not enough to live in, so start from the annotated example:

curl -O https://src.timharek.no/tim/hima/raw/branch/main/hima.example.toml
mv hima.example.toml hima.toml   # then edit it

hima.toml is looked for in the working directory, then in $XDG_CONFIG_HOME/hima (falling back to ~/.config/hima). --config names one explicitly, and HIMA_* environment variables override anything in it.

hima config prints what was actually loaded, from wherever it came. It is the command that still works when the database does not, which is what you want when a path looks wrong.

Homes

A home is which property — the house, the cabin — and every item, tag and store is scoped to one. Homes come from the config: hima.toml decides which exist, hima only keeps rows so the other tables have a stable key.

[[homes]]
name = "Default"
short = "d"
address = "Parkveien 1, 5001 Bergen"

Matching is by short, so renaming a home keeps everything filed under it. A home dropped from the config is never deleted — its data waits for the entry to come back. --home hytta picks which one a command works on; without it, default_home decides.

The day-to-day

Receipts

Drop files into inbox/ under your receipts_path — PDFs, photographs, whatever the shop gave you. hima takes the formats a household actually files as proof: PDF, images (including the .heic an iPhone writes and the multipage TIFF a scanner writes), plain text and RTF, Word and LibreOffice documents, spreadsheets and CSV, and saved emails. Anything else is left exactly where it is and named in the scan output rather than imported — an archive is only readable if it holds evidence and not the notes you keep beside it.

If your scanner writes something else, add it:

extra_document_types = ["webarchive", ".tif"]

It adds and cannot subtract. A household that could switch PDFs off is one typo away from an inbox that silently imports nothing.

hima inbox                     # scan, then say what is waiting
hima inbox triage --view       # open them in your viewer, file them in $EDITOR

hima inbox triage opens one buffer with every waiting document in it, prefilled with what hima could work out: the date and shop from the filename, and the kind and spelling from what you decided about the last receipt named like it. Fix what is wrong, save once, and all of it is applied together. A buffer saved byte-identical is an abort, so quitting out of one you did not mean to open costs nothing.

hima inbox on its own says what is waiting, oldest first and cut at the same --limit the buffer uses, so it is the pile triage is about to offer.

Filing a receipt moves the file out of the inbox, keeping the name it arrived under. A file that is later renamed or moved is recognised by its contents and followed rather than imported twice.

--needs-items, on either command, is the other pile: filed purchases with nothing entered from them yet.

Items

hima items add                             # $EDITOR on a TOML description
hima items add -t "Drill" --value 1499 --tag verktøy --in loftet
hima items add -t "Keycaps" --value 89USD --purchased-at 2025-11-14
hima items 42                              # one item in full
hima items edit 42

Any flag skips the editor, and --editor has them prefill the buffer instead. A value can carry its own currency — what was paid and what it was paid in is one thing, said one way.

Items are never deleted. hima items deactivate 42 marks one gone; something you no longer have was still bought, is still on a receipt, and may still be under warranty.

Searching

The same query language works in hima items, hima items list, hima items triage and hima export items. Everything in a query has to match; bare words match a title or any prop.

tag:verktøy         a plain tag
prosjekt:kjøkken    a tag in a namespace — any namespace works, and one
farge:blå           exists as soon as something is filed under it
in:loftet           where it is: a part of the house
owner:Tim           who it belongs to
store:Komplett      where it was bought
status:sold         active, inactive, broken, lost, sold

Values match on part of a name, so in:kon finds Kontoret. Terms cannot contain spaces.

Triage, and gaps that stay gaps

hima items triage --gaps value

hima items triage offers the items missing something — a value, a date, a store, somewhere they are, tags, an owner, a receipt — with what is missing written above each block. --gaps works one hole at a time, which is how a large backlog actually gets finished: an afternoon of pricing rather than an afternoon of everything.

An item nothing more can be said about — a present with no receipt, a hand-me-down nobody priced — gets leave in the buffer and stops being offered. That answers the question; it does not fill the gap. The item still counts as incomplete, and filling it in later clears nothing. A backlog that can never reach zero is a backlog nobody works through.

The house

A home and a house are the same building seen from two sides: the home is which property, and the house is what that property is made of — rooms, windows, the roof, circuits.

hima house add                 # a buffer of [[part]] blocks
hima house                     # the tree, under the home it belongs to
hima house due                 # what is old enough to worry about

Parts have an age, a condition and an expected life, which is what makes hima house due possible — a question receipts and tags cannot answer. Lifespans per kind are in the config, seeded from the ranges the Norwegian building trade works with; override the ones you disagree with.

Where a thing is is a part of the house, not a tag, and it is single-valued: "where is my drill" has one answer. An --in that names no part is refused rather than created — a typo must not add a wing to the house.

Events

Something that happened to the house: a fence built, a bathroom refurbished, a leak found.

hima events add "Nytt gjerde" --kind improvement --on 2026-05-02
hima events                    # the timeline, newest first
hima events edit nytt-gjerde

An event is a folder under events_path with an index.md in it — frontmatter for what it was and when, markdown for what happened, and the photographs beside it. It names the parts of the house it touched and the receipts it cost, and its cost is the sum of those receipts rather than a number typed twice.

Exporting

hima export items --format pdf -o inventory.pdf
hima export items --format csv in:loftet
hima export receipts --format zip --kind expense -o 2025.zip

export items is what you own — as a PDF it is the document an insurer asks for, grouped, valued, totalled and naming the receipt behind each line. As CSV it is a row per item for a spreadsheet. export receipts is what you spent, and --format zip bundles the documents themselves alongside CSV rows for them.

Both take the same filters as the command they are named after, and both say on stderr what they left out — an export that quietly covers less than it lists is the wrong answer nobody checks.

Money

Amounts are exact decimals, not floats: sums do not drift, and a total that disagrees with a spreadsheet by a few øre costs an afternoon to explain.

A purchase made in another currency keeps what was paid, in the currency it was paid in, plus what that was worth at home on the day it was bought and the rate that got from one to the other. Rates come from Norges Bank by default, or the ECB via Frankfurter, or nowhere at all:

[exchange]
provider = "norges-bank"   # or "frankfurter", or "none"

An amount hima could not convert is stored, shown, left out of every total, and reported. A rate that could not be fetched must not stop a triage session, and a foreign number added into a column of kroner is worse than a gap. A rate you type yourself is recorded as yours, so a figure somebody reasoned about never looks like a fetched one.

What hima will not do to your data

  • A receipt whose file has gone is flagged and reported, never removed. An unmounted disk must not destroy rows.
  • Duplicates and unreadable files are reported, not resolved.
  • Deleting a receipt detaches its items, never deletes them. A thing you own outlives its paperwork. --purge is the one command that deletes a file, and only when you ask for it.
  • Nothing is deleted to tidy up.

Backups

Two things hold everything: the SQLite database (database_path) and the directories it describes (receipts_path, events_path).

Never copy the .db file while hima is writing to it. Take a snapshot with SQLite instead:

sqlite3 /path/to/hima.db "VACUUM INTO '/backups/hima-$(date +%F).db'"

The receipts and events directories are plain files — back them up with whatever you already use. The database must not live in a synced directory: Syncthing and friends corrupt SQLite. The documents themselves sync fine, and sync-conflict files are skipped and named rather than indexed.

To restore, point database_path and the directories at the restored copies and run hima receipts and hima events.

Design notes

docs/design/ holds what has been thought through: what was decided, and as importantly what was rejected and why. remote.md is where the server, the webapp and the phone go.

The web-era version of hima — templ, Tailwind, a server — lives on the web branch. This is the rewrite that came after it.

Contributing

Anyone can contribute. See the contribution guidelines.

Open pull requests, report bugs on the issue tracker.

Licensed under the GPL-3.0. See LICENSE.