Repository navigation
Releases: itambox-community/itambox-webapp
Release list
ITAMBOX v1.0.0-beta.6
Security
- Raised
pypdfto6.21.2andvirtualenvto21.14.12for advisories the vulnerability database published afterv1.0.0-beta.5: four MEDIUM findings in the PDF library, which ships in the release image, and two HIGH findings in the virtualenv development tooling. Both stay inside the rangespyproject.tomlalready declares, and the dependency security gate that had started failing on every pull request passes again.
ITAMbox v1.0.0-beta.5
Compatibility
- Breaking for upgrades: the prerelease migration history is replaced by a named 1.0 baseline. Each app now ships
0001_baselinefollowed by0002_cross_app_relationswhere relations cross apps, and the seed and database-guard migrations (assets.0004_seed_reference_data,0005_core_vocabulary,0006_core_vocabulary_german_translations,0007_asset_type_identity_guard,extras.0003_definition_guards). Theissue88_shard_*, issue-numbered and auto-named migrations are gone, and a fresh install no longer runs the prerelease data conversions. Every baseline migration declares the prerelease identities it replaces, so av1.0.0-beta.5database records the baseline without executing it and then applies only two idempotent clean-ups (extras.0004_drop_legacy_column_default,users.0002_unique_constraint_names). A fresh install and an upgraded database end in the same schema and the same migration-recorder state. The supported upgrade origin isv1.0.0-beta.5; a database fromv1.0.0-beta.4or earlier must first be upgraded to beta.5, andmigration_baseline_preflightrejects it. A plugin migration that names a first-party migration in itsdependenciesmust be updated to the new names.
Added
- Plugins can declare host-mounted REST resources using the Stable core model viewset bases and declare plugin-owned permission targets through
PluginConfig.permission_targets. The host owns authentication, permission and schema defaults, rejects viewset overrides that bypass host authentication, permission checks or scoped list/detail handling, applies core tenant-scope rules after plugin queryset overrides and filter-backend processing, and includes declared targets in SSO role policy, Technician seed permissions and the tenant role matrix. Plugin GraphQL is query-only; writes use the host-owned REST surface.
Changed
- Breaking: The bundled Compose
dbservice now runs PostgreSQL 18, and its named volume is mounted at/var/lib/postgresqlinstead of/var/lib/postgresql/databecause the 18 image keeps its cluster in/var/lib/postgresql/18/docker. An existing 16 data directory is not readable by the 18 image: replacing the image on a running stack starts an empty cluster beside the old files, so an existing installation has to move its data with a dump and restore orpg_upgradebefore it switches. PostgreSQL 16 remains a tested compatibility version; 15 and older are no longer tested. Follow the major-version database upgrade procedure. - The
requestedScopeargument of the GraphQLassetsandassetqueries is now optional and defaults to the scope of the current session; an explicit selector still overrides it and invalid selectors still fail withINVALID_SCOPE. - GraphQL list and detail queries for accessories, consumables, kits, components and licenses now follow the session scope, so a tenant-group or all-accessible session returns the same objects as the REST API instead of only tenantless rows.
- Plugins are configured through
ITAMBOX_PLUGINS_CONFIG(JSON keyed by plugin package) with per-key environment overridesITAMBOX_PLUGIN_<NAME>_<KEY>for secrets, validated against the newsettings_schemamanifest attribute.settings_schemareplacesdefault_settingsandrequired_settings, which a plugin may no longer declare; unknown keys, missing required keys, wrong types and values outsidechoicesabort startup with a message naming the plugin and the key, and keys flaggedsecretnever appear in logs, diagnostics or errors. This includes the message of a startup error raised by plugin code and secret values of any length, including numbers. migration_baseline_preflightrecognizesv1.0.0-beta.5(supported-predecessor-beta5) as the only predecessor state; the earlier pre-squash, transition-release and beta.3 states are removed.scripts/migration_audit.pynow verifies that thereplacesdeclarations on disk and the checked manifest agree, and the supported-upgrade qualification driver isscripts/qualification/migrations/run-supported-upgrade.sh.- The detail tabs and the card-header tabs (object lists, import, user profile) stay on one row and scroll horizontally instead of wrapping into up to three rows on narrow screens. The overflowing edge fades and a chevron marks the direction; the active tab is scrolled into view, and the bar joins the tab order exactly while it can scroll. The native scrollbar stays hidden because it reserves height and would lift the active tab's underline off the card border.
Security
- The user detail, edit and delete pages now enforce the workspace tenant boundary. A non-superuser reaches only users who hold a membership in a tenant of their current workspace scope, exactly as the user list already did, and the permission is evaluated in a tenant the target user is actually a member of rather than in the actor's own tenant; a pk outside the scope answers 404. A password on an existing account is a global credential and is now restricted to superusers, like the staff, superuser and login-capability flags next to it, so it can no longer be reset through that page by a tenant-scoped role; creating a new account keeps its provisioning password.
- Raised the dependency floors that carried open advisories:
Pillowto>=13.0.0,<14.0(was>=12.3.0,<13.0; nine advisories across fourteen alerts, including critical ones),certifito2026.10.19and, through@graphiql/react,markdown-itto14.3.3in the browser bundle. The Pillow floor is a deployment requirement: an existing installation has to reinstall its Python dependencies before it starts.
Fixed
- The bulk check-in, bulk check-out and bulk disposal entries are now offered where the scan pages actually accept them. In the all-accessible scope the mobile action bar offers them when at least one accessible tenant grants the permission, and the docked bulk-action toolbar offers them only to a user who holds it, instead of unconditionally; a concrete tenant scope still answers to that tenant's permission (issue #907).
- Saving a different interface language on the user preferences page now reloads the whole page, so the sidebar, topbar, messages and page title are rendered in the new language immediately instead of staying in the previous one until the next manual reload (issue #909). A save that keeps the language keeps the in-place behaviour.
- The dashboard Change Log widget's Hide Internal Event Entries option now hides the entries it promises to hide: background job bookkeeping (
core.jobchanges) and system-generated changes with no acting user, next to the legacyextras.eventexclusion. The option composes with the widget's action and object-type filters instead of replacing them, its help text states what it hides, and an installation that turns it off keeps the unfiltered feed (issue #913). - The global search page's "Object types" field lists the searchable models again, one entry per registered search index, so a search can be narrowed to a model type from the UI. The choices are read from the live search registry when the form is built instead of being captured at import time, before the per-app search indexes had registered (issue #919).
- Unread notifications stay readable in the dark theme. An unread entry now marks itself with a theme-aware surface and a leading accent instead of a light-only tint plus a pinned dark subject colour, which had painted the subject dark-on-dark (1.1:1 measured) while read entries stayed legible; the membership, role and user-group detail pages also render their value headings in the body text colour instead of a fixed dark one (issue #921).
- The sidebar's Bulk Actions group offers Bulk Check-out and Bulk Check-in again in the all-accessible ("All tenants") scope. The group gated them on an objectless
assets.change_assetcheck, which that scope refuses because it has no tenant anchor, so only Bulk Disposal appeared even though the scan pages accept those actions with a target tenant the actor holds the permission in; the group now uses the same scope-aware decision as the scan views, the mobile action bar and the bulk-action toolbar (issue #923). - Resource grants link their usage page through the standard breadcrumb help button instead of a second, page-local Docs button. The list, revoke and expiry-run views declare
document_path = "usage/resource-grants", so the one help entry point every page already renders points at that page, and the two templates that existed only to add the extra button are gone (issue #925). - The detail tab bar keeps every label visible on desktop again. A rule that collapses an inactive tab to its icon applied at every viewport width and overrode
d-xl-inline, so the Subscriptions tab (and the other generic tabs) showed a bare icon next to model tabs that carry their label; the compact behaviour now applies below thexlbreakpoint only (issue #927). - The camera scanner decodes EAN and UPC again in Firefox. The reader preferred the browser's native
BarcodeDetectorwhenever it was present and the library never falls back on its own, while Firefox's detector answers QR codes but drops the 1D symbologies, so every 1D frame decoded to nothing; the native detector is now only used when it reports the full set the scanner promises (QR plus the 1D codes), and the bundled decoder handles the rest (issue #931). - The theme preference moved from the header into the user menu, where Auto / Dark / Light are one segmented control backed by a labelled radio group instead of two sun/moon buttons you had to cycle through. The workspace switcher now sits in the mobile header as a single briefcase button that opens the tenant list, so the drawer no longer carries a second switcher at its foot (issue #929).
- The user menu no l...
ITAMbox v1.0.0-beta.4
Added
manage.py operational_healthreports worker, scheduler, queue, cache, stuck-job and failure health with--jsonand--check(issue #587). Thresholds are configurable through the new optionalITAMBOX_HEALTH_*settings./health/keeps its database-only readiness contract, now pinned by tests. Read-only; no migrations.manage.py export_catalogue_schemawrites the JSON Schema (2020-12) of a community catalogue asset type, derived from the core specification vocabulary: flat model fields,categorylimited to the vocabulary categories, and aspecsmap limited to vocabulary keys with quantities in their vocabulary unit (for example57 Wh). The generated file is committed next to the generator and a drift test keeps it equal to the vocabulary (issue #769). The community catalogue repositoryitambox-community/type-librarykeeps a byte-identical copy and validates every model file against it (issue #771).- The generic bulk import (preview, confirm, background job) imports catalogue asset types in that format (issue #770): each row's
specsmapping is validated with the same rules as the generated schema before any job runs,categoryandasset_roleare referenced by slug, a missing manufacturer is created only for an actor who may add manufacturers, rows are created through the canonicalcreate_asset_typecommand, and an existing type is skipped and reported, never updated. - The asset overview shows a compact Recent activity card with the five newest timeline events and a link to the full timeline (issue #645).
Changed
-
Breaking: The in-app Type Library is removed (issue #768). The Type Libraries navigation entry, the
/assets/type-libraries/pages (list, import, detail, export) and the REST endpointsPOST /api/assets/type-libraries/preview/,/apply/and/export/are gone and answer 404; library import, release history, reconciliation, snapshots and forks no longer exist. The read-only Library origin is no longer published: thelibraryproperty of the REST Asset Type representation and the GraphQLAssetType.libraryfield with itsLibraryOrigintype are removed, and specification commands no longer lock library rows (the catalogue lock and the owner row lock are unchanged). The core vocabulary document drops its release envelope (kind,library) in favour of a plainversionandlabel; its fields, fieldsets, choices and categories are unchanged. Reusable Asset Type models move to the community catalogue: one YAML file per model, imported through the regular bulk import (epic #772). The specification model is unchanged: managed core fieldsets, quantity kinds and units, ordered specifications with history and the core vocabulary keep working as before. The Asset Type columnslifecycle,deprecated_atandreplaced_byexisted only for the Library and are removed with it (they were neither shown nor filterable), and theextras.manage_specification_librarypermission and the Library model permissions are retired: the upgrade removes them from the permission table and from roles that carried them. No data migration is needed; Library tables are dropped empty. -
Breaking: Repair episodes are replaced by the repair maintenance itself (issue #644). The
RepairEpisodemodel, its/assets/episodes/routes, its navigation entry, its form fields, its generic CSV export declaration and its permissions are removed; a repair is an ordinaryAssetMaintenanceof type repair, and that record anchors the whole story. Recording a repair can issue a loaner in the same submission: the loaner is checked out to the asset's current holder as a loan bound to that maintenance, with the due date defaulting to the expected completion, and the submission fails closed (writing nothing) when the unit is unavailable, belongs to another tenant, or the asset has no holder. Completing the repair offers Returned (the loaner is checked back in and the repaired unit is checked out to the same holder), Replace permanently (the loan stays with the holder as a regular assignment, optionally starting the disposal of the original unit in the same step, linked to the repair) or Do nothing; each reuses the existing checkout, check-in and disposal operations and their permissions, and an action the actor may not perform is not offered.AssetAssignmentandAssetDisposalgain an optionalmaintenancelink and theepisodefields on maintenance, reservation and disposal are gone. The asset timeline groups records by repair maintenance and lists assignments and loans as events, and Log repair on the Timeline tab opens the maintenance quick-add with the repair type preselected. Upgrade: the migration maps every beta-era episode - a linked disposal moves to the episode's single repair maintenance, a linked stand-in becomes that repair's loan, and anything that cannot be mapped unambiguously (no or several repair maintenances, a reservation link, the episode notes, a substitute without a matching loan) is appended to the maintenance notes or written as a journal entry on the asset - reports the count per outcome, and leaves records that cannot be anchored standing and unlinked rather than dropping them. -
Breaking: GraphQL is read-only. All Graphene write mutations (assets, inventory, licenses, software, subscriptions and asset specifications, 54 in total) and their input types were removed, and the built-in schema publishes no mutation root. Writes are REST-only. Migration for API consumers: use the existing REST resources for assets, inventory, licenses, software and subscriptions (including the subscription lifecycle and assignment actions); asset-type composition and category defaults through the
asset-typesactions (composition-preview,create-preview,apply-category-defaults-preview); and the newcustom-field-choice-setsandcustom-field-choicesresources, whose writes (like custom field and fieldset writes) now run through the definition commands withIf-Matchresource revisions. Money values are decimal strings or numbers on REST, which also resolves the formercreateAsset(purchaseCost: 19.99)Decimal failure (issue #612). -
The read-only GraphQL API moved from Graphene to Strawberry:
grapheneandgraphene-djangoare removed and pinned tostrawberry-graphql0.328.x withstrawberry-graphql-django0.92.x (issue #613). The published schema keeps the previous contract unchanged — same/graphql/route, token authentication, locally bundled GraphiQL, introspection for authenticated users, and the same 24 root query fields with the same types, fields, argument names, and nullability. Four documented differences accompany the port: a plugin'sgraphql_schemanow expects StrawberryQuery/Mutationtypes instead of GrapheneObjectTypes (the plugin API version stays 1.0, and a Graphene contribution is rejected and logged as a plugin failure instead of aborting core startup); theDate,DateTime, andDecimalscalar descriptions are reworded with unchanged wire behavior (Date/Decimalremain string scalars,DateTimestays ISO-8601); object types list their declared relations before the model fields and optional arguments print with an explicit null default (after: Cursor = null), leaving the type, field, and argument sets unchanged; and the former 500-field-selection cap is replaced by Strawberry's built-inMaxTokensLimiter(2000 tokens) andMaxAliasesLimiter(50) alongsideQueryDepthLimiter(10) and the unchanged fan-out complexity budget (max complexity 2000, fan-out 10), while operations rejected before execution still answer HTTP 400 without adatamember.assets/tests/test_graphql_schema_snapshot.pynow pins the published SDL (fixtureitambox/assets/tests/data/graphql_schema_text.graphql) together with the root-field contract. -
Generic CSV/YAML/template export is now explicit opt-in and scope-safe (issue #585). Every installed model carries a reviewed data-transfer declaration; an undeclared or denied model answers 404 on every generic export path (CSV, YAML, template render,
all,filteredandpk=scopes) instead of being exportable by default. Each exportable model declares the scoping the export must apply (tenant-scoped manager, shared reference data, container scoping viavisible_to_containers, or owner-scoped personal rows), and the route requires the model's dedicatedexport_<model>permission in addition toview_<model>wherever one is declared, so the generic surface is never weaker than a dedicated export.compliance.CustodyReceiptand the other confirmed or same-pattern surfaces (custody signing sessions and handoff deliveries,compliance.AssetAudit, attachments,core.Job,core.RecycleBin, personal dashboards/bookmarks, saved filters, webhook endpoints and deliveries, sessions, tokens, permissions, OTP devices and background-task tables) fail closed; their dedicated surfaces remain authoritative. Export templates bound to a denied model are hidden from the content-type picker and answer 404 rather than downloading an empty or over-broad file.export_scope=allnow documents and implements "every row the requester may see under their declared scope", never every row in the database. -
reconcile_procurement_legacyreports pre-upgrade fulfilment pledges (requests that a partial receipt approved while delivered quantities were still untracked, with a blankqty_received); it classifies each pledge individually: candidates are pledges on partially received non-serialised lines whose own allocation exceeds the line's recorded received quantity, while completed records (assigned asset or fully received line) stay untouched and pledges the received units could cover in full, lines without receipt, and serialised lines without an asset require explicit operator review ---applyonly softly closes the candi...
ITAMbox v1.0.0-beta.3
Changed
- Release images are published as a multi-platform index with
linux/amd64andlinux/arm64variants instead of an amd64-only image, so ARM64 hosts can pull the official tag directly; the release workflow builds both platforms from one reviewed commit, verifies both platform manifests fail-closed, and boot-checks both platform images before the draft release is prepared (issue #549). - The frontend build toolchain migrated from Node.js 20 to Node.js 26: the
stylelint,frontend, and E2E CI jobs, the runner-validation workflow, the production image's frontend build stage (node:26-slim), and the contributor prerequisites inCONTRIBUTING.mdnow use Node.js 26. - Dashboard charts (asset status distribution and asset age distribution) are rendered with Apache ECharts instead of ApexCharts; categories, colors, tooltips, legends, empty states, light and dark theming, and the behavior across HTMX swaps and GridStack resizes are preserved, and crowded category labels rotate on narrow widgets the way the previous engine rotated them. The chart code is bundled locally with no runtime downloads and no CSP exception was added, and the ApexCharts dependency and its vendored script are removed entirely.
Fixed
- Corrected the maturity statements in the documentation index and the SaaS Subscriptions model page to the declared capability grades: SaaS Subscriptions and Purchase Orders and Contracts are Stable; the report designer, scheduled reports, alert rules, webhooks, the Asset Request procurement seam, and SCIM provisioning remain Beta; the plugin system remains Experimental. No declared capability grade changed; only the published documentation was corrected.
Security
- The published image installs the current
openssl,libssl3, andtzdatasecurity builds in its runtime stage instead of inheriting the stale base-image pins, clearing the medium- and low-severity findings that remained from thev1.0.0-beta.2image scan. - The release gates now block on every unsuppressed image finding (
--fail-on any) instead of only high and critical findings, so a cut can only pass from a scan-clean candidate; governed suppressions remain the only exception. - A scheduled drift scan rebuilds and rescans the current
mainimage weekly, so new advisories surface between releases instead of only at the next release gate.
Known limitations and upgrade requirements
- The supported upgrade origin for this release is
v1.0.0-beta.2. Version skipping remains unsupported, and the earlier prereleases (v1.0.0-alpha.*,v1.0.0-beta.1) are not supported origins — runmigration_baseline_preflightfrom the exact candidate checkout before any migration work.
ITAMbox v1.0.0-beta.2
Added
- The Asset Type Library was finalized on the specification model: asset types compose an explicit, ordered list of fieldsets and fields with per-target applicability, the type-library vocabulary is pinned to a canonical core set, and the runtime serves exactly this vocabulary with no development-era compatibility layer left (issue #479).
- Repair/replacement stories can be reconstructed as episodes: link maintenance records, reservations, and disposals to a repair episode (an asset plus an optional loaner/substitute) and read the grouped story on the asset detail Timeline tab (issue #504).
- Asset holders gain a read-only Offboarding tab that gathers everything a departing person still holds across assets, inventory, licenses, custody receipts, subscriptions, and memberships; it replaces the removed
offboard_user.pyhelper (issue #498). - Tagged releases now also publish the official container image to the GitHub Container Registry at
ghcr.io/itambox/itambox-webapp:1.0.0-beta.2: the release workflow pushes the exact image it built, scanned, archived, and SBOM-validated, verifies the registry digest against that image, and attaches build provenance and the SPDX SBOM to it as OCI attestations. The downloadable archive, the standalone SBOM, and the registry image all describe the same reviewed commit.
Changed
- Merged
subscriptions.Providerinto the sharedassets.Suppliercatalogue, now scoped by tenant or tenant group with null representing global scope; moved portal, account, and active fields onto suppliers, and made subscriptions reference suppliers directly with an optional procurement contract link. The cutover is a deterministic, upgrade-only migration with no compatibility aliases. This is a breaking prerelease change (issue #508). - Added supplier links for SaaS subscriptions and asset warranties, documented when to use Contracts versus Subscriptions, and distinguished agreement entitlement quantities from linked-license seat counts in subscription details and reports (issue #500).
- Added the read-only
migration_baseline_preflightrelease gate and checked manifest. The gate recognizes the current normalized baseline and the two supported predecessor states (supported-predecessor-pre-squash,supported-predecessor-transition-release) as exit-0 outcomes; partial, old, or mixed states are rejected before a cleanup attempt, and restore-first handling for interrupted non-atomic migrations is documented. - Normalized the migration history for the #479 release: the two #479 development waves and the replaced issue-#88 originals are gone, fresh installs and both supported predecessors (pre-squash and transition release) migrate to the same final schema, and the checked baseline manifest moves to the normalized layout (issue #479). The supported-upgrade qualification driver is checked in at
scripts/qualification/migrations/run-supported-upgrade-p1p2.sh. - Forwarded client-IP handling is explicit and opt-in in production:
ITAMBOX_RATELIMIT_USE_X_FORWARDED_FORandITAMBOX_RATELIMIT_NUM_PROXIESconfigure trusted-proxy client IPs, while directly reachable deployments stay fail-closed. - Release preparation from
mainnow requires the app-owned end-to-end qualification suite to pass on the dispatched commit before the draft release is prepared. - Scope switching preserves list context: search, filters, and paging survive a switch, scope-bound filters are dropped with a visible notice instead of an empty list, a switch from an object page lands on the list instead of a 404, and aggregate bulk confirmations name each object's tenant. The platform-wide scope is labelled All Tenants (superusers: Global View), and users can choose a personal default workspace for a tenant, tenant group, or aggregate view (issue #499).
- Tightened the first organization/membership English copy: role-form guidance, the assign-users explanation, member-selection help, role presets, and user-group help are shorter and more direct; the role assignment page and the permissions-matrix help now render reviewed German translations (part of issue #386).
- The first German asset UI text chunk uses the established glossary terms and clearer labels for asset types, locations, custody receipts, and warranties.
- Tightened the first English asset/request text slice: selection errors, request labels, custody notifications, disposal guidance, and bulk receiving copy are shorter and more direct without changing behavior.
- The desktop footer keeps its timestamp and version stamp without the optional Tabler theme credit; the full application footer is hidden below the mobile action-bar breakpoint.
Fixed
- The demo seed dataset now tells stories the product can actually produce: a received purchase-order line materialises an asset for every unit it reports, a repair episode is preceded by the check-in that removed the device from its holder (and the device is handed back afterwards with the status it had before the re-checkout), out-of-service maintenance documents a repair window the asset's own change log recorded, and an approved asset request always carries a tenant and an allocated unit that is actually claimable. Repaired units are no longer left stranded in a repair episode whose window never closed, and seeded assignments can no longer run backwards, start before their asset was bought, or return from the future. The self-check reads a repair window from the repair label itself rather than the
pendingmeta-type that In Transit and Quarantined share, closes the window at the transition that leaves repair instead of at the asset's last change of any kind, and treats an assignment that overlaps the window at any point as a contradiction rather than only one open when it began; a unit already allocated to an open request is left unreserved so that a reservation for a different holder cannot dead-end the claim. A seed self-check fails closed when any of these regresses (issue #506). - The demo seed writes no placeholder signature payloads for accepted custody receipts, and the full-seed qualification covers the custody-receipt story end to end (issue #407).
- Disposal history is preserved through explicit cancellation: a cancelled disposal keeps its record with reason, actor, and timestamp, the asset returns to Pending, a later disposal becomes a new historical record, and effective report totals exclude cancelled rows while the history stays visible (issue #496).
- The dashboard target picker offers every canonically authorized live tenant (including managed-only MSP reach), renders each target independently of the ambient tenant, and fails closed for inaccessible, deleted, or revoked targets instead of falling back to ambient data (issue #447).
- The asset creation form now accepts a blank asset tag and generates one from the tag sequence on save, matching the model contract and the PO receiving, bulk receive, import, and clone paths (issue #503).
- Scan baskets now report an EAN that maps to several assets distinctly instead of claiming no asset matches, directing the operator to scan the asset tag (issue #502).
- Subscription annual cost now annualizes multi-year terms over their length and one-time purchases are labeled as a one-time cost instead of occupying the annual slot; zero-cost subscriptions display
0.00instead of an omitted row or "Not set" (issue #501). - Component Allocation create and asset quick-add are now explicitly target-only, reject silently ignored source locations, return observable HTMX success/errors, count source-backed Component checkouts only once in availability, and keep component/source/destination immutable on update (issue #393).
- Tenant-scoped pages purge pre-existing HTMX session history snapshots and no longer save new ones, preventing stale asset actions from targeting objects outside the current server-side scope or retaining tenant DOM after a workspace switch (issue #419).
- Asset list pagination now limits assignee resolution and tenant-scoped related-manager work to the rendered page instead of evaluating every asset in the active scope (issue #416).
- Asset edits no longer clear purchase and in-service dates when localized HTML5 date controls are submitted after changing the tenant, location, or another field (issue #391).
- Managed-tenant onboarding now gives the creating provider administrator explicit administrator reach and switches directly into the new tenant without adding a customer membership.
- Tenant creation now exposes only object-authorized live provider tenants to eligible non-superusers, requires an explicit provider when one is available, and uses the same onboarding projection for normal and managed-tenant routes (issue #405).
- Asset reservation quick-add now preserves a native POST fallback from asset detail pages instead of submitting a GET request (issue #390).
- List page refreshes no longer emit an HTMX
htmx:oobErrorNoTargetconsole error when the applied-filter count badge is updated: the filter-toggle badge now carries thefilters-applied-counttarget id that the list refresh out-of-band swap expects, so applying, clearing, loading, or saving list filters update the visible count cleanly (issue #421). - Bulk check-in, check-out, and disposal keep their pre-seeded baskets when a target tenant is selected in an aggregate scope; every submitted asset is validated as a live asset of the bound tenant before any job is created, and job lists and cancellation follow the accessible-tenant scope in aggregate scopes (issue #424).
- Non-staff users with tenant-scoped asset request permissions can now see and use their authorized actions consistently in tenant, tenant group, and All-accessible scopes: fulfilment actors record the handover from the request detail page, the bulk-receipt toolbar follows the per-request decision, and malformed or repeated selections report a message instead of an access-denied page (issue #497).
- A...
ITAMbox v1.0.0-beta.1
Added
- Scheduled reports with a cross-tenant scope now have an operator approval workflow: the schedule list shows the scope approval state, and a dedicated Scope Approval page approves or revokes the durable authorization (
reports.view_cross_tenant_reports). Revocation keeps the approval history and fails delivery closed until a fresh approval covers the full scope. - Resource grants can expire: expiry dates drive a system revocation sweep, and a scoped audit API exposes the grant-expiry history (issue #195).
- Webhook delivery is durable and observable: deliveries run through a state machine with retry handling and typed per-attempt outcomes instead of fire-and-forget sends (PR #349).
- The production GraphQL surface enables schema introspection for authenticated users and exposes an authenticated GraphiQL interface (PRs #372, #376).
Changed
- The report-designer opt-in flag is now
ITAMBOX_FEATURE_REPORT_DESIGNER;ITAMBOX_REPORT_DESIGNER_ENABLEDremains a deprecated 1.x fallback. When the flag is off, scheduled delivery is skipped for non-grandfathered templates, while the migration-managed bounded grandfathered set continues to render and deliver; grandfathered templates are read-only until the designer is enabled. - Event Rule conditions were withdrawn for 1.0: unsupported conditions fail closed and existing rows are preserved (issue #187).
- Alert-channel delivery now records typed, observable outcomes per delivery attempt (issue #185).
- Tenant-surface SCIM group reads require the
users.view_usergrouppermission (issue #193). - Accessibility was qualified across full-page and HTMX interaction journeys, and the remaining review gaps were closed (issue #101).
- The runtime image refreshes its CA-certificate bundle during the build (issue #370).
Fixed
- API token lifecycle: the key is shown once at creation, responses carry ETags, deletion works without a queryset, owner transfer returns 200, and creation without an active tenant fails closed (issues #341, #353).
- User configuration and asset-tag-sequence endpoints reject unknown request fields instead of silently ignoring them (issue #344, PR #354).
OPTIONSon collection endpoints no longer raises 500 (issue #340).- API root and API namespace root discovery return 200 for authenticated users instead of 500 (issues #345, #363).
- Scanner lookup resolves across all accessible tenants and accepts EAN/GTIN codes in every scan flow (issue #367).
- Frontend: page headers stack actions on mobile, the mobile header/footer order and sidebar search are corrected, the dashboard switcher aligns with the action row, and mobile dashboard changelog cards carry field headings (PRs #365, #366, #368, #373, issue #374).
Security
- Dashboard creation through the API sets the owner from the request context and rejects owner spoofing (PR #352).
- The runtime image pins a patched nanoid dependency instead of the vulnerable override.
ITAMbox v1.0.0-alpha.3
Added
- Added a shared typed external-integration error contract with retryable/terminal classification, safe user messages, bounded Graph retry handling, structured tenant/actor/request context, and documented follow-up boundaries for other adapters.
- Added the versioned webhook envelope v1 with additive
schema_version,event_id,delivery_id,attempt, and owning-tenantfields across generic, Slack, and Teams payloads; strict additional-property validators must update their schemas. - Published the 1.x compatibility, deprecation, and support policy together with a bounded external-contract inventory covering REST/GraphQL/SCIM surfaces, the webhook envelope, persisted choice values, contract-bearing settings, permission codenames, UI URL namespaces, and each capability's contract class and exclusions. A stdlib gate derives every enumerated surface from source and fails when the published contract and the code disagree.
- Added explicit Purchase Order lifecycle endpoints at
/api/procurement/purchase-orders/{id}/approve/,/order/,/receive/,/cancel/, and/reopen/. - Published the bounded procurement Stable qualification matrix, including existing UI, REST, service, tenant, permission, audit, currency, and PostgreSQL concurrency guarantees plus the deliberately absent surfaces.
- Added explicit, opt-in Event retention controlled by
ITAMBOX_EVENT_RETENTION_DAYS, with observable pruning of expired events. - Added opaque SCIM resource IDs and per-domain scoped
externalIdvalues; the previous identifier scheme remains readable through a 1.x dual-read window.
Changed
- Intune discovery now records typed integration failures with safe user-facing messages and structured tenant-scoped job-log context; optional detected-software degradation is explicit in the completed result as
software_degraded. - Qualified the four-state Subscription lifecycle as Stable across model, UI, REST, GraphQL, import/export, assignment, seat-accounting, and daily-task boundaries, including idempotent retries and model-level tenant validation.
- Subscription status is now a closed four-state lifecycle (
active,suspended,cancelled,expired) driven by explicit UI, REST, GraphQL, admin, and background actions. The canonical renewal-term field isvendor_contract_auto_renews;auto_renewalremains a 1.x read/write API compatibility alias. - Purchase Order
statusand Purchase Order Lineqty_receivedare read-only in the REST schema. API clients must use the corresponding lifecycle endpoint: differing direct writes now return HTTP 400 with sanctioned-action guidance, while identical values remain accepted and ignored for full-representation PUT compatibility. Existing rows require no migration. - Asset Request auto-approval and the Beta Asset Request procurement seam are now opt-in through
ITAMBOX_REQUISITION_AUTO_APPROVAL_THRESHOLDS; fresh deployments leave requests pending. The legacy setting name remains a deprecated 1.x fallback with a startup warning. - Alert rules with no configured channels now deliver nowhere instead of implicitly fanning out to every enabled tenant or tenant-less channel; platform-global in-app delivery remains explicit.
- Closed the Event action vocabulary:
EventRule.eventsis validated against the supported action set ahead of the 1.0 contract freeze. - Scheduled reports are gated on the report designer opt-in: with the designer disabled, the navigation entry is hidden, all scheduled-report routes return 404, and the background task fails closed while preserving saved rows.
- Completed the zero-error OpenAPI generation contract and the generated-client compatibility check.
- Updated locked runtime dependencies to redis-py 8.1 and django-redis 7.0.
Fixed
- Made invalid generic list filters fail closed to an empty result while retaining field-level validation errors across full-page, HTMX, direct-query, and saved-filter requests.
- Serialized concurrent Purchase Order approvals so only one draft-to-approved transition succeeds, and preserved the Purchase Order currency on assets materialized by receiving.
- Scheduled-report runs now preserve their failure status and the original exception, keeping failed deliveries observable.
- Corrected the alert-channel delivery documentation to match shipped behavior (synchronous, single-attempt delivery).
- Made SCIM capability metadata truthful: removed the fake HTTP Basic capability, fixed
User.lastModifiedhandling, and wired request-id propagation. - Custom (non-CRUD) permissions declared via
Meta.permissionsare now exposed in the role editor, making actions such as preparing and exporting custody receipts grantable through the UI. - A valid signing session now overrides the seven-day custody bearer-link TTL, restoring assisted handoff on receipts older than seven days.
- Fixed custody handoff UI issues: QR codes have a proper quiet zone, the handoff panel is mobile-safe, actions wrap on small screens, Bootstrap collapses rebind after HTMX swaps, copy feedback is sticky with a clipboard fallback, and custody export links are no longer htmx-boosted.
- Fixed frontend issues: app-external links (docs, API, GraphQL, admin) are no longer htmx-boosted, the report preview button no longer leaks across pages, theme-token changelog panels and mobile stacking render correctly, Purchase Order and Resource Grant labels are localized, the responsive asset shell cleanup is complete, and scanner camera detections are throttled through one shared gate.
Security
- Hardened the Intune Graph boundary against cross-registration token-cache reuse, bearer forwarding through redirects or untrusted
@odata.nextLinkhosts, provider-controlled device-ID path pivots, stale token lifetimes, and credential/payload leakage in exception and job logs. - Added cross-tenant read/write matrices for contracts, Purchase Orders, and Purchase Order lines together with real two-connection receipt and approval race tests.
- Centralized Asset Request-to-Purchase Order linking in a tenant-locked, permission-checked, idempotent service and made failed UI linking roll back the new purchase order.
- Removed the unused global pip installation and ensurepip bootstrap from the production runtime image, with build-time checks that the copied runtime environment remains pip-free, while preserving locked uv-based dependency resolution in the builder.
- Global cross-tenant report aggregation is now permission-gated; the curated built-in report contract remains unchanged.
- Froze and proved the tenant resource-grant security boundary with cross-tenant read/write matrices and adversarial coverage.
- Removed the remaining unsafe-inline style dependency from the Content Security Policy and restored runtime dashboard styles under the hardened policy.
- Remediated the dependency security-gate findings in the locked dependency set.
ITAMbox v1.0.0-alpha.2
Added
- Added deterministic CI ratchets for test coverage and certification, changed-code coverage, local imports, exception handling, OpenAPI diagnostics, architecture layers, and import cycles. Existing debt is explicit and cannot grow silently.
- Added an architecture decision record and contributor guidance for module layers, approved import directions, inline-import annotations, exception boundaries, and the supported public plugin API.
- Added focused security and authorization regression coverage for generic views, membership and role-grant services, login and tenant SSO entry points, inventory stock actions, and asset-form scoping.
Changed
- Standardized Python formatting and import ordering with Ruff, reduced selected function complexity, and paid down selected findings while preserving the existing deterministic flake8 no-growth ratchet.
- Stabilized generic detail, list, create, edit, delete, and service-action views around fail-closed authorization checks, and standardized HTMX success responses for restore and service actions.
- Extracted membership and role-grant operations from presentation forms into explicit organization services with tenant-, container-, and delegated-scope enforcement.
- Consolidated accessory, consumable, and component stock actions on shared transactional services and authorization checks.
- Decomposed
AssetForminitialization into focused collaborators while preserving tenant scoping, initial-value precedence, and validation behavior. - Made OpenAPI generation deterministic and checked in the canonical schema plus a warning/error identity baseline.
- Made the daily subscription expiry/reminder task enumerate all tenants and use the local date, avoiding tenant omissions and one-day boundary drift.
- Updated locked dashboard and Django integration dependencies to GridStack 13.1.2, django-filter 26.1, django-htmx 1.28.0, and django-otp 1.7.0; refreshed frontend build and browser-test tooling and added browser coverage for GridStack initialization, resizing, and persisted layouts.
Fixed
- Hardened login and tenant SSO entry points against stale tenant selection, unsafe fallback behavior, ambiguous provider routing, and signing-algorithm drift; corrected the documented tenant OIDC routes.
- Replaced security-sensitive silent exception handling with explicit, justified boundary behavior and a blocking policy gate.
- Removed an accidental internal gap report from release source archives.
- Updated PostCSS and brace-expansion dependencies past their reported security advisories.
Security
- Added fail-closed authorization tests around shared action views, membership and RBAC mutations, tenant selection, SAML POST handling, and OIDC provider validation.
- Escaped configurable
AssetFormtag-prefix help text so crafted values cannot break HTML attributes or inject active SVG markup. - Made
domain-model -> presentationimports unconditionally forbidden and made new module-top or deferred import cycles fail CI. - Required every retained broad/silent exception, local import, cycle, and cross-layer dependency in the scanned production scope to carry a reviewable identity or inline justification.
Known limitations and upgrade requirements
- The architecture, exception, local-import, OpenAPI, lint, and coverage baselines intentionally freeze pre-existing debt; they prevent growth but do not claim that the recorded debt is already removed.
- Upgrades from deployments that used arbitrary passphrases in
ITAMBOX_FIELD_ENCRYPTION_KEYSmust carry forward the exact previously derived Fernet key. Substituting a replacement key makes existing encrypted secrets unreadable; follow the installation guide before deploying this alpha. ITAMBOX_TENANT_LDAP_CONFIGS,ITAMBOX_TENANT_SAML_CONFIGS,ITAMBOX_TENANT_OIDC_CONFIGS, andITAMBOX_TENANT_INTUNE_CONFIGSmust contain JSON objects. Malformed JSON or non-object values now stop startup withImproperlyConfiguredinstead of silently disabling the integration configuration.- Review tenant-specific SAML and OIDC mappings before upgrading. SAML requests now require a live, configured tenant; a
defaultSAML configuration is accepted only when exactly one live tenant makes it unambiguous, and missing, deleted, or inactive tenant bindings return 404. - SaaS subscriptions, procurement, reporting, webhooks and event rules, SCIM, and the plugin lifecycle remain Beta. Their interfaces may change during the prerelease series.
- Alpha upgrades may include breaking migrations. No general version-skipping policy exists yet; review and test the exact target revision with a complete backup and rollback plan.
- The full pytest suite is not safe to run with
pytest-xdist; use the default serial configuration. - SQLite is not supported. PostgreSQL 15 or newer is required for development, tests, and production.
ITAMbox v1.0.0-alpha.1
ITAMbox 1.0.0-alpha.1
This is the first tagged alpha of ITAMbox: a self-hosted, multi-tenant IT asset-management platform for internal IT teams and managed-service providers.
It is a limited-audience prerelease. The repository is public, but this release is intended for evaluation and early feedback—not a production-support commitment.
What you can do today
- Manage the asset lifecycle: Catalog hardware, assign and check assets in or out, reserve equipment, track warranties, maintenance, depreciation, disposal, and cost history.
- Run location-aware inventory: Manage accessories, consumables, components, and kits with barcode/QR workflows, transactional stock operations, imports, exports, and bulk actions.
- Track software and spend: Maintain software catalogs, installed-software records, license seats, suppliers, cost centers, subscriptions, purchase orders, contracts, and fulfillment links.
- Operate across tenants safely: Use tenant roles, groups, delegated resource grants, and scoped administration for MSP and multi-organization environments.
- Support custody and audits: Create custody receipts with digital sign-off, run audit campaigns, reconcile records, preserve audit evidence, and export results.
- Integrate and extend: REST APIs with OpenAPI/Swagger UI/ReDoc, a scoped GraphQL API, CSV tooling and Snipe-IT migration support, LDAP/SAML/OIDC sign-in, Intune discovery sync, and Beta SCIM provisioning and plugin extension points.
Built for accountable operations
This alpha includes auditable change records, tenant-aware background jobs, search, tags, custom fields, dashboards, reports, alerts, webhooks, notification channels, and a German-localized PWA experience.
The deployment path is a production-oriented Docker Compose stack with PostgreSQL, Valkey, a background worker, health checks, and an isolated smoke test. Python dependencies are locked with uv so Docker, CI, and contributors resolve the same versions.
Security and release qualification
ITAMbox enforces tenant boundaries across UI, REST, GraphQL, imports, bulk actions, attachments, and downloads. This release also includes hardened delegated authorization, hashed and rotatable API tokens, encryption for protected settings and secrets, webhook SSRF defenses, TOTP enforcement for privileged local accounts, rate limiting, SAML replay protection, secure upload validation, and a nonce-based script CSP.
The tagged commit passed the full hosted CI suite, E2E and Docker smoke tests, security and CodeQL gates, a release-image Trivy scan, and independent archive checksum verification. Backup/restore and migration-parity qualification are documented as part of the alpha baseline.
Before you evaluate
- Use PostgreSQL 15+. SQLite is not supported.
- Prefer the documented Docker Compose path; the release archive is a reviewed image candidate, not a public container-registry image.
- Treat upgrades as potentially breaking during the alpha series. Take a complete backup and test the exact target revision before upgrading; version skipping is not supported yet.
- SaaS subscriptions, procurement, reporting, webhooks/event rules, SCIM, and the plugin lifecycle are Beta and may change before 1.0.
- The full test suite currently runs serially;
pytest-xdistis not supported.
Documentation and technical detail
- Getting started and deployment
- Development and operations documentation
- Complete technical changelog
Please evaluate in a disposable or fully backed-up environment and report feedback through GitHub Issues.