CLI tool for German tax submissions and document retrieval via the ELSTER ERiC library. Website and docs: viking.capocasa.dev.
Supported forms:
| Kürzel | Full name | Subcommand |
|---|---|---|
| UStVA | Umsatzsteuervoranmeldung | viking ustva |
| EÜR | Einnahmenüberschussrechnung | viking euer |
| ESt | Einkommensteuererklärung | viking est |
| USt | Umsatzsteuererklärung | viking ust |
The Anlagen below ride along inside viking est. Capital gains are not
missing, they just don't get their own subcommand (Anlage KAP is part of the
ESt return, not a Steuerart):
| Kürzel | Full name |
|---|---|
| KAP | Kapitalerträge (capital gains/investment inc.) |
| S | Selbständige Arbeit (freelance) |
| G | Gewerbebetrieb (trade/business) |
| R | Rente und sonstige Leistungen (pensions) |
| Kind | Kinder (child allowances) |
| VOR | Vorsorgeaufwendungen (insurance premiums) |
| SA | Sonderausgaben |
| AgB | Außergewöhnliche Belastungen |
Also viking iban (Änderung der Bankverbindung), viking message
(Sonstige Nachricht to the Finanzamt), and viking list/viking download
for Steuerbescheide from the ELSTER Postfach.
This is experimental — tax submissions are irreversible, so verify independently.
Prebuilt binaries live on the releases page:
viking-linux, viking-macos (universal) and viking.exe. Afterwards viking fetch pulls the ERiC runtime (about 340 MB).
Linux:
mkdir -p ~/.local/bin
curl -fsSL -o ~/.local/bin/viking \
https://github.com/capocasa/viking/releases/latest/download/viking-linux
chmod +x ~/.local/bin/vikingIf ~/.local/bin isn't on your PATH yet: bash users put it in ~/.profile
(login shells, so it reaches everything you launch; Debian and Ubuntu's stock
.profile adds it automatically, just re-login after creating the dir). zsh
never reads .profile, so zsh users take ~/.zshrc:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.profile # bash
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc # zshmacOS: same steps with viking-macos and ~/.zshrc (the default shell).
Windows, one paste into PowerShell. Installs to ~\bin, puts it on your user
PATH, prints the version:
md "$env:USERPROFILE\bin" -Force | Out-Null; curl.exe -fsSL -o "$env:USERPROFILE\bin\viking.exe" https://github.com/capocasa/viking/releases/latest/download/viking.exe; $p = [Environment]::GetEnvironmentVariable('Path','User'); if (-not ($p -split ';' -contains "$env:USERPROFILE\bin")) { [Environment]::SetEnvironmentVariable('Path', "$p;$env:USERPROFILE\bin", 'User') }; & "$env:USERPROFILE\bin\viking.exe" --versionOpen a new terminal afterwards; running ones keep their old PATH. ERiC also needs the Microsoft VC++ Redistributable, which stock Windows ships without (viking tells you when it's missing):
winget install Microsoft.VCRedist.2015+.x64Then on any OS:
viking fetchnimble build
./viking fetch # downloads and extracts the ERiC libraryWire your ELSTER signing certificate (.pfx) and PIN explicitly via [auth]:
[auth]
cert = viking.pfx ; path to the .pfx (relative to this conf's dir)
pin = viking.pin ; plaintext PIN file (or the PIN itself, inline)
; pincmd = pass show elster/pin ; or: any shell command that prints the PIN on stdoutExactly one of pin= or pincmd= is required. pin= takes either a path to a file containing the PIN or the PIN text itself (inline is fine for the public sandbox PIN, not recommended for checked-in real confs). pincmd= is any shell command — runs with the conf's directory as cwd.
# UStVA (quarterly VAT advance) — amounts come from the source's euer= TSV
viking ustva -s freiberuf --period 41
# EÜR (profit/loss statement) for a named source
viking euer -s freiberuf
# ESt (income tax return) — aggregates every source
viking est
# USt (annual VAT return) for a named source
viking ust -s freiberuf
# Retrieve documents from Finanzamt (Steuerbescheide etc.)
viking list # show available documents
viking download # download all
viking download Steuerbescheid_2024.pdf # download specific file(s)
viking download -o ./bescheide # save to specific directory
# Dry run: validate via ERiC and print XML, don't send
viking ustva -s freiberuf --period 41 --dry-run
# Sandbox submission (uses ELSTER test endpoint; XML gets a Testmerker)
viking ustva --test -s freiberuf --period 41
# Use a different conf
viking ustva --conf ./client-foo/viking.conf -s freiberuf --period 41The tax year comes from [steuerzahler].year in viking.conf, not the CLI — copy the conf dir per year.
The download command queries the ELSTER Postfach, downloads documents from the OTTER server via libotto, and sends the mandatory confirmation (PostfachBestaetigung). Existing files are skipped unless --force is given.
viking.conf is an INI file. Four section names: [steuerzahler] (taxpayer, plus an optional second one for the spouse), [kind] (per child), [einkommen] (per income source — typ= dispatches) and [auth]:
[steuerzahler]
name = Hans Maier
year = 2025 ; required — copy this dir per tax year
steuernr = 1234567890123
idnr = 04452397687
strasse = Musterstr.
nr = 1
plz = 10115
ort = Berlin
iban = DE89370400440532013000
abzuege = abzuege.tsv ; optional: ESt Abzüge TSV
[steuerzahler] ; second taxpayer → co-filing spouse
name = Greta Maier
idnr = 04452397688
geburtsdatum = 12.07.1956
[einkommen] ; typ=freiberuflich → Anlage S
typ = freiberuflich
versteuerung = ist
euer = freelance.tsv
[einkommen] ; typ=gewerbe with company name → Anlage G, rechtsform=gmbh
typ = gewerbe
name = Musterfirma GmbH
versteuerung = soll
euer = musterfirma.tsv
[einkommen] ; typ=kapital → Anlage KAP
typ = kapital
name = ibkr
guenstigerpruefung = 1
pauschbetrag = 1000
[einkommen] ; typ=rente → Anlage R
typ = rente
rente = rente.tsv
[kind]
name = Lena Maier
verhaeltnis = leiblich
geburtsdatum = 15.03.2019
idnr = 02293417683
[auth] ; signing material (required for live submit)
cert = viking.pfx
pin = viking.pin ; or inline, or `pincmd = pass show elster/pin`Rules: duplicate section names are allowed (parsecfg loop mode). The first [steuerzahler] is you (required: year = YYYY); any later [steuerzahler] with a different name= is your co-filing spouse (Zusammenveranlagung). [einkommen].typ picks the Anlage — freiberuflich (S), gewerbe (G), kapital (KAP), rente (R). For typ=gewerbe, an optional company name= with a trailing legal-form suffix (GmbH, UG, KG, OHG, GbR, PartG, eK, eG, KGaA, SE, "GmbH & Co. KG", …) picks the Rechtsform; otherwise it's an Einzelgewerbe. Company sections are accepted today but only EÜR is wired — full double-entry bookkeeping (Bilanz / E-Bilanz) is future work. External files are wired explicitly: EÜR sources declare their income/cost TSV via euer= (optional — zeros + warning if unset); rente sources declare their row TSV via rente=; the taxpayer declares the ESt abzuege= TSV; [auth] points at the .pfx (cert=) and either a PIN source (pin= file or inline) or a shell command (pincmd=). No filesystem scanning, no year interpolation, nothing implicit — copy the conf dir per year for clean data. See viking init for a full template and docs.rst for the slow tour.
The conf is loaded from:
~/.config/viking/viking.conf(global defaults)./viking.conf(per-directory overrides)--conf <path>(explicit — replaces the chain)
The [auth] section is required for live submissions — point cert= at the .pfx and set exactly one of pin= or pincmd=.
pin=— if the value resolves to an existing file, viking reads it (plaintext PIN file). Otherwise it treats the value as the PIN itself (inline). Inline is fine for the public sandbox PIN (123456) but don't check it in for real confs.pincmd=— any shell command, runs with the conf's directory as cwd. Stdout is the PIN.
[auth]
cert = viking.pfx
; pin = viking.pin ; path to plaintext PIN file
; pin = 123456 ; or inline (sandbox only)
; pincmd = ./viking.pin.sh ; or a local script
; pincmd = pass show elster/pin ; or a secret manager
; pincmd = security find-generic-password -s elster -w ; macOS Keychain
; pincmd = secret-tool lookup app elster ; libsecretAny shell snippet that prints the PIN on stdout works — pass, 1Password CLI, macOS Keychain, libsecret, gpg, age, cat, whatever.
viking fetch installs ERiC under ~/.local/share/viking/ (Linux), ~/Library/Application Support/viking/ (macOS), or %APPDATA%\viking\ (Windows). Override with --data-dir <path> on any subcommand.
Layout:
<data-dir>/
eric/... # extracted ERiC runtime (only the current platform's files)
logs/ # ERiC log output
Grab the public test certificates from ELSTER:
wget https://download.elster.de/download/schnittstellen/Test_Zertifikate.zip
unzip Test_Zertifikate.zipPick one of the .pfx files (softpers for personal, softorg for business), point your viking.conf's [auth] at it, write 123456 into the matching .pin file, and run with --test:
viking submit --test --period 41 --amount19 0 --conf ./test-viking.confnim c -r tests/test_e2e.nimRequires ERiC installed (viking fetch) and the test certificates placed under <data-dir>/certificates/.
MIT