BiblioViva Documentation
Technical documentation

Configuration reference

Generated from the shipped folder config/ (config.example.json + classification.json + labels/stocks/*.json, merged) by bun run scripts/config-reference.ts; do not edit by hand. The schema (src/lib/server/config/schema.ts) validates every key at startup and bun run config:check checks a folder without starting the server, as "Import configuration" does with an archive (the schema, the templates, the pictograms, the reports, the language packs). Keys starting with _comment are ignored. Secrets never live here (environment variables, see src/env.ts).

The configuration folder

CONFIG_DIR (the /config volume) is a folder, not one file (PLAN.md §3.6, Q78). Everything below is merged at startup and on every reload into the single document described by the table; the merge is deterministic and bun run config:check --explain <key.path> prints which file defines a key.

PathContentsMerge rule
config.jsonthe core sections (written by the first-run wizard)base document
<section>.json (e.g. classification.json, circulation.json)one top-level sectiondeep-merged over the same section of config.json; the fragment wins (a warning says so when both define it)
calendar.<year>.jsonholidays and closedRanges of one yearappended to calendar
labels/stocks/<id>.jsonone adhesive stock (id defaults to the file stem)appended to labels.stocks, replacing a stock with the same id
{cards,labels,slips}/templates/<name>/one print template per folderdiscovered by name, never merged into JSON
reports/<name>.jsonthe school's own report definitions; a file with the id of a built-in replaces it (the built-ins are read from the application, ADR-014)discovered by id
classification/images/pictograms, uploaded from Administration → Configuration → Classification or copied by handdiscovered by code
branding/logo.pngthe library's logo (step 4.10), uploaded from Administration → Appearance and languageshown at the top left of the app when present
history/<change>/the previous version of every file a change from the admin UI touched, with change.json (step 11.1); the last 50 changes are keptnever loaded; used to restore a previous version

The editors of Administration → Configuration (Phase 11) write each section back to the file it comes from, atomically, after validating the whole folder with the same schema; the classification (classification.json and its pictograms) is the first one (step 11.1). Editing the files by hand keeps working.

To add a stock or a template, copy a file or a folder and edit it. The defaults are copied into CONFIG_DIR at the first start when missing; config.json alone is never enough on its own because the classification section lives in its fragment.

Keys

KeyExample / typeNotes
librarysection
library.name"Biblioteca dell'Istituto"
library.contactEmail"biblioteca@scuola.example"
library.schoolYearStart"09-01"
library.schoolYearLabel"span"
library.timezone"Europe/Rome"
library.weekStart"monday"
library.itemTypesarray of 5 object(s), e.g. {"code":"libro","label":"Libro"}
schoolsectionPhase 12 (Q94): the levels of the school in order, grouped in stages, from the preset it-primaria (config/school-presets/). The classes themselves (1 A, 3 B, the teachers group) are data, created in Administration → Classes.
school.preset"it-primaria"
school.stagesarray of 1 object(s), e.g. {"code":"primaria","name":"Primaria"}
school.levelsarray of 5 object(s), e.g. {"code":"1","label":"1","name":"Prima primaria","stage":"primaria"}
school.classLabel"{level} {group}"
school.groupWord"Sezione"
school.cardClass"{word} {group}"
school.promotionWithinStage"keep"
school.promotionAcrossStages"ask"
catalogsectionQ73: author and publisher are mandatory; these special values are offered as quick picks when there is no personal author or the publisher/place is unknown.
catalog.specialAuthorsarray, e.g. ["Anonimo","AA. VV."]
catalog.unknownPublisher"s.n."
catalog.unknownPlace"s.l."
barcodessectionPhase 12 (Q90-Q93): new copies YYYY-CNNNN and new cards CNNNNNN with a mod-23 check letter (2026-E0267, U012345). The codes of the old system keep working: copies YYYY-NNNN, cards of 10 digits, plain numbers up to plainMaxLength digits [VERIFY at the Koha migration].
barcodes.keyboardLayout"it"
barcodes.cameraSound"confirm"
barcodes.plainMaxLength9
barcodes.itemobject
barcodes.item.formatobject
barcodes.item.format.pattern"YYYY-CNNNN"
barcodes.item.format.check"mod23"
barcodes.item.legacyarray of 1 object(s), e.g. {"pattern":"YYYY-NNNN","check":"none"}
barcodes.item.symbology"code128"
barcodes.patronobject
barcodes.patron.formatobject
barcodes.patron.format.pattern"CNNNNNN"
barcodes.patron.format.check"mod23"
barcodes.patron.legacyarray of 1 object(s), e.g. {"pattern":"NNNNNNNNNN","check":"none"}
barcodes.patron.symbology"code128"
barcodes.patron.counterStart"afterLegacyMax"
circulationsectionQ13 resolved: students 15 days / 1 loan / 5 renewals, teachers 15 days / 4 loans / 5 renewals. Q68: DVDs 5 days and 1 renewal, whoever borrows them.
circulation.rulesobject
circulation.rules.studentobject
circulation.rules.student.loanDays15
circulation.rules.student.maxLoans1
circulation.rules.student.maxRenewals5
circulation.rules.student.renewalDays15
circulation.rules.teacherobject
circulation.rules.teacher.loanDays15
circulation.rules.teacher.maxLoans4
circulation.rules.teacher.maxRenewals5
circulation.rules.teacher.renewalDays15
circulation.itemTypeOverridesobject
circulation.itemTypeOverrides.dvdobject
circulation.itemTypeOverrides.dvd.loanDays5
circulation.itemTypeOverrides.dvd.maxRenewals1
circulation.itemTypeOverrides.dvd.renewalDays5
circulation.dueDateMode"datedue"
circulation.renewFrom"today"
circulation.allowRenewalWhenOverduetrue
circulation.overdueNoticeobject
circulation.overdueNotice.template"overdue-by-class"
circulation.overdueNotice.pageBreakPerGroupfalse
circulation.overdueNotice.minDays5
circulation.blockIfOverduefalse
circulation.hardDueDatenull
circulation.receiptsfalse
calendarsection
calendar.weeklyClosedDaysarray, e.g. [0,6]
calendar.autoItalianHolidaystrue
calendar.holidaysarray, e.g. ["2026-11-02"]
calendar.closedRangesarray of 2 object(s), e.g. {"from":"2026-12-23","to":"2027-01-06","reason":"Vacanze di Natale"}
z3950section
z3950.defaultTarget"sbn"
z3950.timeoutMs15000
z3950.maxRecordsIsbn20
z3950.maxRecordsSearch20
z3950.targetsarray of 1 object(s), e.g. {"id":"sbn","name":"OPAC SBN","host":"opac.sbn.it","port":2100,"database":"nopac
metadatasectionQ26 resolved: only the public OPAC SBN Z39.50 service by default. Optional providers: openlibrary, googlebooks, sbn-json (undocumented mobile gateway).
metadata.providersarray, e.g. ["sbn"]
metadata.cacheDays30
metadata.timeoutMs8000
performancesectionQ22, step 9.28: micro = one slow core and 512 MB; small = Raspberry Pi 4 with 2 GB and SD card (default); medium = old PC with a spinning hard disk; large = PC with plenty of RAM and SSD. Any key below overrides the profile default (the values of each profile: src/lib/shared/performance.ts).
performance.profile"small"
performance.sqliteCacheMbnull
performance.sqliteMmapMbnull
performance.walAutocheckpointPagesnull
performance.imageConcurrencynull
performance.yazConcurrencynull
performance.heavyConcurrencynull
performance.argon2MemoryMbnull
performance.smolnull
cardssectionDefaults reproduce the library's current cards (docs/reference/cards): four units per A4, crop marks in the margins and gaps, one template for every patron type.
cards.defaultTemplate"default"
cards.templateByPatronTypeobject
cards.layout"side-by-side"
cards.duplexobject
cards.duplex.flip"long-edge"
cards.duplex.backOffsetMmobject
cards.duplex.backOffsetMm.x0
cards.duplex.backOffsetMm.y0
cards.cardMmobject
cards.cardMm.width85.6
cards.cardMm.height53.98
cards.bleedMm0
cards.sheetobject
cards.sheet.paper"A4"
cards.sheet.rows4
cards.sheet.marginMm6
cards.sheet.gapMm3
cards.sheet.foldMarkstrue
cards.sheet.cutMarkstrue
cards.sheet.cutLinesnull
cards.validityMonths12
cards.showPhototrue
labelssectionAdhesive stocks live one per file in labels/stocks/.json (Q78): copy a file to add a new stock.
labels.default"pair"
labels.titleMaxChars30
labels.monochromefalse
labels.pairobject
labels.pair.template"pair-default"
labels.pair.stock"a4-70x48"
labels.barcodeobject
labels.barcode.template"barcode-default"
labels.barcode.stock"a4-70x48"
labels.spineobject
labels.spine.template"spine-default"
labels.spine.stock"a4-70x48"
labels.stocksarray of 4 object(s), e.g. {"id":"a4-70x24","name":"Etichette 70 × 24 mm (3 × 12, mezza altezza)","paper":"
photossectionenabled: false switches patron photos off altogether (9.38). Step 3.11: every patron photo is stored at exactly width × height (cropped at the centre when the browser has not cropped it) in format (jpeg or webp) with quality 1–100. required: false hides every reminder that photos are missing (they can still be taken and printed).
photos.enabledtrue
photos.requiredtrue
photos.width480
photos.height640
photos.format"webp"
photos.quality80
photos.maxUploadBytes8000000
photos.sheetobjectPhase 13: pictures with many faces for the photo census (the photographer's sheet, the class's cards on a table). maxUploadBytes: the largest file accepted; maxPixels: the longest side kept; maxFrames: at most this many photos per picture; minAspect / maxAspect: the height / width of a photo frame.
photos.sheet.maxUploadBytes32000000
photos.sheet.maxPixels4000
photos.sheet.maxFrames60
photos.sheet.minAspect1
photos.sheet.maxAspect1.6
coverssectionenabled: false switches book covers off altogether (9.38). Step 3.11: covers keep their proportions (never cropped): width in pixels, optional maxHeight, format jpeg or webp, quality 1–100. Q27: cover providers tried in this order, each with its own flag. Amazon has no API (may stop working); Google Books needs the GOOGLE_BOOKS_API_KEY environment variable, hence off by default.
covers.enabledtrue
covers.width600
covers.maxHeightnull
covers.format"webp"
covers.quality80
covers.maxUploadBytes12000000
covers.providersarray of 3 object(s), e.g. {"id":"amazon","enabled":true}
covers.country"IT"
covers.minWidth300
covers.autoFetchtrue
covers.timeoutMs6000
privacysectionRemoval of a patron anonymizes it immediately (Q32/Q33); there is no retention timer.
privacy.auditRetentionMonths24
backupsection
backup.cron"0 3 * * *"
backup.catchUptrue
backup.keepDays30
backup.offsiteobject
backup.offsite.enabledfalse
backup.offsite.remote"/offsite/biblioviva-backups"
securitysectionAccounts are normally shared per role. passwordHash from bun run auth:hash-password; a plain password key is accepted with a warning. At least one active account must hold admin.
security.sessionDays14
security.loginMaxAttempts5
security.loginWindowMinutes15
security.authobject
security.auth.mode"config"
security.usersarray of 5 object(s), e.g. {"username":"admin","displayName":"Amministratore","roles":["admin"],"passwordHa
security.rolesobject
security.roles.adminarray, e.g. ["*"]
security.roles.circulationarray, e.g. ["loans"]
security.roles.catalogingarray, e.g. ["catalog"]
security.roles.membershiparray, e.g. ["patrons"]
security.roles.reportingarray, e.g. ["reports"]
uisectionlocale: a built-in language (en, it) or the code of a language pack in CONFIG_DIR/i18n// (validated at startup, English fills the gaps). theme: the colour set; dashboard: the sections of the start page (quick actions always first), each true or false. Both are chosen in Administration → Appearance and language.
ui.locale"en"
ui.theme"ocean"
ui.dashboardobject
ui.dashboard.quickActionstrue
ui.dashboard.tilestrue
ui.dashboard.inventorytrue
ui.dashboard.installHinttrue
ui.dashboard.dueThisWeektrue
ui.dashboard.todayActivitytrue
ui.checkoutIdleSeconds120
ui.setupCompletedtrue
classificationsectionScheme taken from the library's Koha CCODE list (docs/reference/classification): single-letter genres for fiction, Dewey-like codes grouped by hundred for non-fiction, one pictogram per genre and per hundred (children inherit). The shipped pictograms are generated (bun run pictograms:generate, step 11.14) in the colour of each node; replace them from the classification editor.
classification.fictionByClasstrue
classification.readingLevelsarray of 5 object(s), e.g. {"code":"1","label":"1ª"}
classification.classPerItemfalse
classification.genresarray of 14 object(s), e.g. {"code":"A","label":"Romanzi e Racconti","image":"A.svg","color":"#e8231b"}
classification.nonfictionarray of 9 object(s), e.g. {"code":"000","label":"Generalità","image":"000.svg","color":"#f6c744","children
classification.deweyMaparray of 48 object(s), e.g. {"prefix":"004","code":"004"}
classification.callNumberobject
classification.callNumber.fiction"{{genre}} {{author3}}"
classification.callNumber.fictionWithClass"{{genre}} {{class}} {{author3}}"
classification.callNumber.nonfiction"{{code}} {{author3}}"

A–Z index