ShahinLabs ShahinLabs

Assetory

v1.0.0

Asset Management and Tracking System with Flutter Mobile App

Web Installation

Everything needed to get the Assetory website running — server requirements, and full walkthroughs for a VPS, Hostinger and cPanel.

Thank you for purchasing Assetory — asset management and tracking, with a Flutter mobile app. This section takes the website from a bare server to a working install, on a VPS or on shared hosting.

  • Version: v1.0.0
  • Framework: Laravel 13 + Inertia + Vue 3
  • Database: MySQL or MariaDB
No Composer or Node.js needed to install. The download is ready to upload: the PHP packages (vendor/) and the built front end (public/build/) are already included. Composer and Node.js only matter if you change the source — see Modification & Development Setup.
Install the website before the mobile app. The app connects to this site and needs two values only an installed site can give you — its address and its API key. Mobile Installation picks up from there.

Server Requirements

The server stack, PHP extensions, folder permissions and cron Assetory needs.

Before installing, make sure your server meets the requirements below. The installer checks the PHP version, the extensions and the folder permissions for you, and will not continue until each one passes.

  • PHP 8.3 or higher
  • MySQL 5.7+ or MariaDB 10.3+
  • Web server: Apache or Nginx
  • Cron, able to run a command every minute

Required PHP extensions

All of the following must be enabled. Most hosts ship them by default; on cPanel you can toggle them under Select PHP Version → Extensions.

  • OpenSSL, PDO, PDO MySQL, Mbstring, Tokenizer, Ctype, Filter, Session, cURL, Iconv, Zlib, Zip, XML, DOM, SimpleXML, XMLReader, XMLWriter, GD, Exif, Fileinfo
Tip: enable intl as well if your host offers it. It is not required, but without it dates and numbers look less local, and System health will mention it.

A database of its own

Create an empty database and a user with full privileges on it. Note the database name, the username and the password — the installer asks for all three.

The installer deletes every table already in the database. It builds the schema from scratch, so never point it at a database another app is using. Give Assetory a database of its own.

Folder permissions

These must be writable by the web server before the installer will continue:

  • storage/framework — 0775
  • storage/logs — 0775
  • storage/app — 0775
  • bootstrap/cache — 0775
  • .env — 0664 (the installer creates it from .env.example)
Tip: the installer's Folder Permissions step has a Fix Permissions button that sets these for you, so you rarely need to do it by hand.

Document root

Where possible, point the domain's document root at the public/ folder rather than the project root — that is the safest setup, and the one the VPS guide uses.

On shared hosting you often cannot change it. That is fine: upload everything into public_html. The .htaccess in the project root sends every request into public/ and refuses to serve .env and other dotfiles, so the source and your settings stay private.

HTTPS

Install an SSL certificate before running the installer. The installer saves whichever address you visit it on, and the mobile app refuses plain http:// for any server on the internet.

Cron

One cron entry, running every minute, drives everything that happens on a schedule: Upcoming alerts, emails, automatic subscription renewals, webhook retries, depreciation and nightly cleanup. Each guide below shows where to add it on that host.

Without cron the site works, but nothing happens on its own. No alerts go out and no renewals are recorded. A banner and the System health page tell the admin when cron has stopped.

VPS Setup

Step-by-step installation on a VPS or dedicated server with Nginx, PHP-FPM and MySQL.

This guide is for a VPS or dedicated server where you have SSH access. The commands are for Ubuntu 24.04, which ships PHP 8.3; other distributions differ only in package names.

Tested on: Ubuntu 24.04 LTS with Nginx, PHP 8.3-FPM and MySQL 8.0.

Step 1 — Install the server stack

sudo apt update
sudo apt install nginx mysql-server unzip \
  php8.3-fpm php8.3-mysql php8.3-mbstring php8.3-xml php8.3-curl \
  php8.3-zip php8.3-gd php8.3-intl

The remaining extensions (OpenSSL, Tokenizer, Ctype, Exif, Fileinfo and the rest) are part of PHP's core packages on Ubuntu, so the installer finds them without anything extra.

Step 2 — Create the database and user

sudo mysql
CREATE DATABASE assetory CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'assetory'@'localhost' IDENTIFIED BY 'choose-a-strong-password';
GRANT ALL PRIVILEGES ON assetory.* TO 'assetory'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Step 3 — Upload the files

Upload the website package to the server and unpack it into /var/www/assetory, so that artisan sits directly inside that folder:

sudo mkdir -p /var/www/assetory
sudo unzip web.zip -d /var/www/assetory

Check the folder depth. If the archive unpacks into a sub-folder, move its contents up one level. Nginx must find /var/www/assetory/public/index.php.

Step 4 — Set ownership and permissions

The installer writes .env into the project root and needs storage/ and bootstrap/cache/ writable, so hand the folder to the web server user:

sudo chown -R www-data:www-data /var/www/assetory
sudo chmod -R 775 /var/www/assetory/storage /var/www/assetory/bootstrap/cache

Step 5 — Point Nginx at /public

Create /etc/nginx/sites-available/assetory:

server {
    server_name assetory.example.com;
    root /var/www/assetory/public;
    index index.php;
    charset utf-8;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    client_max_body_size 64M;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_hide_header X-Powered-By;

        # Fix: "upstream sent too big header" (502) on large pages
        fastcgi_buffer_size 32k;
        fastcgi_buffers 16 32k;
        fastcgi_busy_buffers_size 64k;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

Enable it and reload:

sudo ln -s /etc/nginx/sites-available/assetory /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Tip: client_max_body_size is what lets photos, receipts and the item import file through. Raise upload_max_filesize and post_max_size in /etc/php/8.3/fpm/php.ini to match, then restart php8.3-fpm.

Step 6 — Enable HTTPS

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d assets.example.com

Certbot adds the certificate to the server block and renews it automatically. Do this before the installer, so the site address is saved as https:// from the start.

Step 7 — Run the web installer

Open https://assets.example.com/install in your browser and follow the steps:

  1. Welcome — start the setup.

  2. Server Requirements — PHP version and extensions. Everything must be green.

  3. Folder Permissions — use Fix Permissions if anything is red.

  4. Site Information — the site name and the application URL. The URL must be the exact https:// address people will use.

  5. Database Setup — host 127.0.0.1, port 3306, and the database, user and password from Step 2. The connection is tested before you can continue.

  6. Install — builds the database, creates the app key and the mobile API key, and links public/storage.

The finish screen shows the administrator login: admin@example.com / 12345678. You must choose a new password the first time you sign in. Once finished, the installer locks itself and /install no longer opens.

Step 8 — Add the cron job

Edit the web server user's crontab:

sudo crontab -u www-data -e

and add this single line:

* * * * * cd /var/www/assetory && php artisan schedule:run >> /dev/null 2>&1

Step 9 — Switch the server type to VPS

Sign in and open Settings → Server. Choose VPS / dedicated and save. Turn on Force HTTPS on the same screen.

No Supervisor or queue worker to set up. In VPS mode, emails and webhooks wait in a database queue, and the same cron job works that queue every minute. On shared hosting mode nothing is queued and jobs run straight away instead.

Troubleshooting on a VPS

"502 Bad Gateway"

Nginx cannot reach PHP-FPM. Check the socket path in fastcgi_pass matches ls /run/php/, and that php8.3-fpm is running: sudo systemctl status php8.3-fpm.

The home page loads but every other page is "404 Not Found"

The try_files line is missing or wrong. Every request that is not a real file must fall through to /index.php.

Logos and avatars do not show

The public/storage link is missing. Run php artisan storage:link as www-data, or press Fix it now on the System health page.

"Permission denied" in the log

A file was created as root — usually by running php artisan with sudo. Run the chown from Step 4 again, and run artisan as www-data: sudo -u www-data php artisan ….

Hostinger Setup

Step-by-step installation on Hostinger shared hosting — no SSH or command line needed.

This is the path for shared hosting without SSH. Assetory runs on Hostinger's shared plans with no Composer, no Node.js and no command line — everything is done in hPanel and the browser.

Works on: Hostinger Single, Premium and Business plans (PHP 8.3+, MySQL, native Cron Jobs included on all tiers).

Step 1 — Set PHP version and extensions

  1. Log in to Hostinger hPanel.
  2. Go to Websites → Manage for your domain.
  3. Set the PHP version to 8.3 or newer via Advanced → PHP Configuration.
  4. Open the PHP Extensions tab and make sure these are enabled (most are by default):
    OpenSSL, PDO, pdo_mysql, Mbstring, Tokenizer, Ctype, Filter, cURL, Iconv, Zlib, Zip, XML, DOM, GD, Exif, Fileinfo, intl

Step 2 — Enable SSL (HTTPS)

  1. Go to Security → SSL and install the free SSL certificate for your domain.
  2. Turn on Force HTTPS.
Tip: do this before running the installer. The installer saves the address you visit it on, so your site URL is stored as https:// from the start — and the mobile app needs HTTPS.

Step 3 — Create the MySQL database and user

  1. In hPanel, go to Databases → MySQL Databases.
  2. Fill in a database name, a username and a password, then click Create — Hostinger creates the database and its user together, with full privileges.
  3. Note these 3 values:
    • Database name — e.g. u123456789_assetory
    • Username — e.g. u123456789_admin
    • Password — the one you chose
Important: Hostinger prefixes both the database name and the username with your account ID. Copy the full prefixed values, not what you typed.

Step 4 — Upload the files

  1. Go to Files → File Manager in hPanel.
  2. Open public_html/ — your domain's root.
  3. Delete any default index.html or default.php in it.
  4. Click Upload and upload the website ZIP from the package.
  5. Right-click the ZIP → Extract, then delete the ZIP.

When you are done, artisan, .htaccess and the app, public and storage folders must sit directly inside public_html.

Check for an extra folder level. If the extract created public_html/web/…, open that folder, select everything, and Move it up into public_html. Turn on Show hidden files first so .htaccess and .env.example come along.

Step 5 — Leave the document root at public_html

Hostinger points your main domain at public_html, and you do not need to change it. The .htaccess you just uploaded forwards every request into public/ and blocks .env and the other private files from being downloaded.

Step 6 — Set folder permissions

In File Manager, right-click each folder → Permissions and set 775:

  • storage (apply to sub-folders)
  • bootstrap/cache
Tip: the installer's permission step can usually fix these itself with Fix Permissions. Set them by hand only if that button reports it could not.

Step 7 — Run the web installer

Open https://your-domain.com/install and follow the steps:

  1. Server Requirements — everything must be green. A red extension means going back to Step 1.
  2. Folder Permissions — press Fix Permissions if anything is red.
  3. Site Information — your site name, and the application URL exactly as visitors will type it, with https://.
  4. Database Setup — host localhost, port 3306, and the three prefixed values from Step 3.
  5. Install — builds the database and creates the app and mobile API keys.

Log in with admin@example.com / 12345678; you must choose a new password straight away. The installer then locks itself.

Step 8 — Set up the cron job

  1. In hPanel, go to Advanced → Cron Jobs.
  2. Choose Custom and enter the command, using your own account ID and domain:
    /usr/bin/php /home/u123456789/domains/your-domain.com/public_html/artisan schedule:run
  3. Set every field to * — once a minute — and save.

Within a minute or two, System health should report the cron job as running.

Keep Settings → Server on "Shared hosting". That is the default, and it is the right choice here: jobs run straight away instead of waiting for a queue worker your plan does not have. Choosing VPS on shared hosting means emails and notifications are never sent.

Troubleshooting on Hostinger

"500 Internal Server Error" after upload

Almost always the PHP version. Check it is 8.3 or newer in PHP Configuration — the installer cannot even start on older PHP.

Uploaded logos and avatars do not show

Some hosts block the public/storage link the installer creates. Open System health and press Fix it now next to Storage link. If it still fails, ask Hostinger support to link public/storage to storage/app/public.

QR labels or email links point at the wrong address

The site URL saved during install does not match the address you use — often http:// against https://, or with and without www. System health shows the line to put in .env as APP_URL.

cPanel Setup

Step-by-step installation on any cPanel host, using File Manager only.

This guide covers any host that uses cPanel. As with Hostinger, no SSH, Composer or Node.js is needed.

Step 1 — Set PHP version and extensions

  1. In cPanel, open Software → Select PHP Version (or MultiPHP Manager on some hosts).
  2. Set the version to 8.3 or newer for your domain.
  3. On the Extensions tab, tick:
    pdo_mysql, mbstring, curl, iconv, zip, xml, dom, simplexml, xmlreader, xmlwriter, gd, exif, fileinfo, intl

Step 2 — Enable SSL (HTTPS)

Open Security → SSL/TLS Status and run AutoSSL for your domain. Most cPanel hosts include it for free. Wait until the domain shows a valid certificate before you install.

Step 3 — Create the MySQL database and user

  1. Open Databases → MySQL Database Wizard.
  2. Create a database, e.g. assetory.
  3. Create a user with a strong password.
  4. On the privileges screen, tick ALL PRIVILEGES and click Next Step.
Important: cPanel prefixes the database and the user with your account name, e.g. myacct_assetory. Use the full names in the installer. Skipping the privileges screen is the usual cause of "Access denied".

Step 4 — Upload the files

  1. Open Files → File Manager and go to public_html (or the folder of an addon domain or subdomain).
  2. Click Settings (top right) and tick Show Hidden Files.
  3. Upload the website ZIP, right-click it → Extract, then delete the ZIP.
  4. Make sure artisan and .htaccess sit directly in the folder, not in a sub-folder.
Tip — addon domains and subdomains: cPanel lets you choose their document root. Under Domains, point it at the public folder (e.g. public_html/assets/public) for the cleanest setup. For the main domain, leave it at public_html; the root .htaccess takes care of it.

Step 5 — Set folder permissions

In File Manager, select storage and bootstrap/cache, click Permissions, and set 775. Or leave it to the installer's Fix Permissions button.

Step 6 — Run the web installer

Open https://your-domain.com/install and work through the steps: Server Requirements → Folder Permissions → Site Information → Database Setup → Install. Use localhost as the database host and the prefixed names from Step 3.

Log in with admin@example.com / 12345678 and choose a new password when asked.

Step 7 — Set up the cron job

  1. Open Advanced → Cron Jobs.
  2. Under Common Settings, choose Once Per Minute.
  3. Enter the command, replacing myacct with your cPanel username:
    /usr/local/bin/php /home/myacct/public_html/artisan schedule:run >> /dev/null 2>&1
  4. Click Add New Cron Job.
Some hosts allow cron every 15 minutes at most. Assetory still works — alerts and renewals simply go out a little later. System health shows a warning rather than an error in that case.

Troubleshooting on cPanel

"Access denied for user" (SQLSTATE 1045)

The user was created but never added to the database. Open MySQL Databases, find Add User To Database, pick both, and grant ALL PRIVILEGES. Also check you used the prefixed names.

"403 Forbidden"

The root .htaccess was not uploaded — it is hidden by default. Turn on Show Hidden Files and check it is in the same folder as artisan.

"500 Internal Server Error"

Check the PHP version is 8.3 or newer for this domain — MultiPHP can set a different version per domain. Then check storage/logs/laravel.log in File Manager for the exact error.

Install stops part-way

The Install step rebuilds the database from scratch, so an attempt that stopped half-way leaves nothing worth keeping. Fix the cause shown in storage/logs/laravel.log — usually a timeout or a permission — and press Install again.

Scheduled tasks never run

Open System health. If Cron job says Never ran, the command path is wrong. Ask your host for the full path to PHP 8.3 — on some servers it is /opt/cpanel/ea-php83/root/usr/bin/php rather than /usr/local/bin/php.

Modification & Development Setup

For buyers who want to edit the website source: PHP, Composer, Node.js, running locally, Wayfinder, building and deploying your changes.

Only needed if you change the source. Running the site as shipped never needs any of this.

Step 1 — Install the tools

ToolVersion
PHP8.3+ with the extensions from Server Requirements
Composer2.x — getcomposer.org
Node.js20.19+ or 22.12+ — nodejs.org
MySQL / MariaDBa local database for development

Step 2 — Set up the project locally

composer install
npm install
cp .env.example .env
php artisan key:generate
php artisan api-key:generate

Open .env and set APP_ENV=local, APP_DEBUG=true and your DB_* values. Then build the database and skip the web installer:

php artisan migrate --seed
echo installed > storage/app/installed.lock
The lock file is what closes the installer. Without storage/app/installed.lock every page redirects to /install.

Step 3 — Run the development server

composer dev

This starts three things together: the PHP server, a queue listener and Vite with hot reload. Open the address php artisan serve prints, usually http://127.0.0.1:8000.

Step 4 — Where to change things

  • resources/js/pages/ — one Vue page per screen, in folders named after the menu.
  • resources/js/components/ — shared components; the sidebar is built in resources/js/composables/useSidebarNav.ts.
  • app/Http/Controllers/Admin/ — thin controllers; the business logic lives in app/Services/.
  • app/Reports/ — the built-in reports, registered in ReportRegistry.php.
  • routes/web.php — the website; routes/api.php — the mobile app's API.
  • public/lang/<code>/ — screen text; lang/<code>/ — emails, notifications and server messages.
  • app/Support/RecordAccess.php — the rules for who sees which record.

Step 5 — Regenerate the Wayfinder route helpers

The front end never types a URL by hand. It calls routes and controllers through Laravel Wayfinder, which generates TypeScript helpers into resources/js/routes, resources/js/actions and resources/js/wayfinder. Whenever you add, rename or remove a route or a controller method, regenerate them:

php artisan wayfinder:generate --with-form

--with-form also generates the .form variants the Vue forms use with Inertia's <Form> component — leave it out and those forms stop compiling.

The generated folders are not in the package's Git history. They are ignored in .gitignore and rebuilt from your routes, so never edit them by hand — your changes are overwritten on the next run.
Tip: the Vite plugin runs the same command each time composer dev or npm run build starts, so PHP must be on your PATH for both. Run it yourself after changing routes while Vite is already running, and before npm run types:check.

Step 6 — Build for production and upload

php artisan wayfinder:generate --with-form
npm run build

Upload the new public/build/ folder to your server, replacing the old one. PHP changes are uploaded as they are; if you added Composer packages, upload vendor/ as well.

Never upload .env or storage/app/installed.lock from your machine. They belong to each install. Overwriting the live .env replaces its database password and keys.

Optional — code quality checks

composer test         # Pint + Pest
npm run lint:check
npm run types:check

Demo mode

APP_DEMO=true in .env blocks every create, update and delete, and resets the sample data every three hours from cron. Set it before installing to also get the demo data; it skips the forced password change. Use it only for a public demo site.

Troubleshooting

"Property 'form' does not exist" during build

The route helpers were generated without form variants. Run php artisan wayfinder:generate --with-form, then build again.

"Cannot find module '@/routes/…'" or a route helper is missing

The helpers are older than your routes. Run php artisan wayfinder:generate --with-form. If the command itself fails, run php artisan route:list — a broken route or controller stops generation.

npm install fails

Check node --version. Node 20 older than 20.19, or Node 22 older than 22.12, is refused by the build tools.

composer install reports a platform requirement error

Your local PHP is older than 8.3, or an extension is missing. php -m lists the loaded extensions.

Changes do not appear on the live site

Front-end changes need php artisan wayfinder:generate --with-form, npm run build and a fresh public/build/ upload. For PHP and settings changes, clear the cache from Settings → Cache.


Mobile Installation

Building the Assetory app from source — toolchain, connecting it to your server, rebranding, push notifications and signed release builds.

The Assetory mobile app is a Flutter app for Android and iOS that works with your installed website. This section takes it from the source folder to a signed build on your own store listing.

  • Version: v1.0.0
  • Framework: Flutter, with Riverpod for state, go_router for navigation and Dio for the API
  • Platforms: Android and iOS
Each build talks to one server. The website's address and its API key are compiled into the app, so a build made for one site will not connect to another. Install the website first — see Web Installation — then build the app for it.
The app keeps nothing of its own. Items, people, photos and everything else live on your server. The app is a window onto it, with the same roles and the same rules about who sees what as the website.

Toolchain

The SDK versions the app builds against, and fetching its packages.

The app is a standard Flutter project, so a working Flutter install is the main prerequisite. The versions below are what the project is built against.

ToolVersion
Flutterstable, with Dart 3.13.4 or newer — flutter --version shows both
Java17 — for the Android build
Androidminimum Android 7.0 (API 24)
Gradle9.3.1 · AGP 9.1.0 · Kotlin 2.4.0
iOS15.0+ — needs a Mac with Xcode

Confirm your machine is ready, then fetch the packages once from the mobile/ folder:

flutter doctor
flutter pub get
iOS cannot be built on Windows or Linux. You can develop and build for Android anywhere, but an iPhone build and an App Store upload both need macOS with Xcode.

Install Flutter from docs.flutter.dev if you do not have it yet.

Connecting to Your Server

The two values from the website that every build needs, and the version controls that go with them.

The app needs two values from your website: its server address and its API key. Both are on the website under Settings → Mobile app.

Step 1 — Copy the values from the website

  1. Sign in to the website as admin.
  2. Open Settings → Mobile app.
  3. Copy the Server address, then use Show key and Copy key for the API key.
No API key shown? The installer normally creates one. If the page says there is none yet, press Generate new key. On the server it is API_KEY in .env.

Step 2 — Paste them into the app

Open lib/core/app_config.dart and replace the sample values:

static const serverUrl = 'https://assets.example.com';
static const apiKey = 'paste-your-api-key-here';
  • The address is the site's root — no /api/v1 on the end.
  • Use https:// for a server on the internet. Plain http:// is only accepted for a server on your own network (192.168.x.x, 10.x.x.x, *.local), because over the internet it would send passwords unencrypted.
While the file still holds the sample values, the app opens on a screen saying it isn't set up yet. That is deliberate — it is the app telling you this step is not done, not an error.

Step 3 — Run it

flutter run

The app opens on the sign-in screen. Sign in with any login from the website.

Changing the API key later

The server refuses every call without the right key. Generate new key on the website therefore stops every build made with the old one, until you put the new key in app_config.dart and build again.

Only regenerate the key if it has leaked. Users on the old build cannot connect until they install the new one.

Minimum and latest version

The same settings page holds two version numbers that the app compares with its own:

  • Minimum version — an app older than this shows Update required and cannot be used until it is updated.
  • Latest version — an app older than this shows an optional A new version is available notice.
  • Google Play link and App Store link — where the Update button sends people.
Raise these only once the new build is live in the stores. Raising the minimum first locks everyone out with nowhere to update from.

Rebranding

The changes required before you can publish under your own name, and the optional ones.

The app ships as Assetory with the id com.shahinlabs.assetory. The first three steps are required before you publish; the last two are yours to choose.

#StepRequired?If you skip it
1App id / bundle idMustBoth stores reject the upload — the id is already taken
2App nameMustThe launcher and sign-in screen say "Assetory"
3Launcher iconMustYour app ships with the demo logo
4Notification iconOptionalPush notifications show the default shape
5Accent coloursOptionalUsers keep the built-in palette
Once signed in, the app shows your site's name. The name set in the website's Settings → General is loaded from the server, so step 2 only covers the launcher and the screens shown before sign-in.

Step 1 — App id / bundle id

Currently com.shahinlabs.assetory. Change it in these places:

  • Android — android/app/build.gradle.kts: namespace and applicationId.
  • Android — android/app/src/main/kotlin/com/shahinlabs/assetory/MainActivity.kt: change its package line to match namespace, and move the file into the folder of your id.
  • iOS — ios/Runner.xcodeproj/project.pbxproj: every PRODUCT_BUNDLE_IDENTIFIER, or Xcode → Runner → General.
Leave name: asset_tracker in pubspec.yaml alone. It is the code's internal package name. Phones and stores never show it, and changing it breaks every import in the project.

Step 2 — App name

  • lib/core/app_config.dart — appName, shown on the sign-in screen and anywhere else before the server's site name has loaded.
  • android/app/src/main/AndroidManifest.xml — android:label, the name under the Android icon.
  • ios/Runner/Info.plist — CFBundleDisplayName, the name under the iOS icon, and CFBundleName, the short name (15 characters at most).

Step 3 — Launcher icon

The icon is built from two 1024×1024 PNGs in assets/icon/, and a new logo means replacing both:

FileHoldsUsed by
app_icon.pngthe finished square, background included — no transparency; the App Store rejects an alpha channeliOS, and Android 7 and older
app_icon_foreground.pngthe logo alone, on transparencyAndroid 8+ and the startup screen

Then set the colour behind the logo — adaptive_icon_background in pubspec.yaml, currently #F2F4F8 — and generate every size:

dart run flutter_launcher_icons
dart run flutter_native_splash:create

The first writes every Android and iOS icon size; the second rebuilds the startup screen, which shows app_icon_foreground.png.

Replacing only app_icon.png is the easy mistake. Android 8+ phones keep the old logo and nothing reports an error.
Tip: Android caches launcher icons, so uninstall the app and install it again to see a change. assets/icon/README.md has the full size rules for both files.

Step 4 — Notification icon

The Android status bar icon is android/app/src/main/res/drawable/ic_notification.xml — a white shape on transparency. Replace it with your own. Its tint is notification_color in android/app/src/main/res/values/colors.xml.

Step 5 — Accent colours

lib/theme/accent.dart holds the palette users pick from in the app's settings — Emerald, Ocean, Violet, Sunset and Rose. Edit the entries, or cut the list down to your brand colour.

Push Notifications

Optional: notifications on the phone even when the app is closed, through Firebase Cloud Messaging.

Push notifications are optional. Without them, the app still shows notifications while it is open. With them, phones get the same notifications as the website's bell even when the app is closed, through Firebase Cloud Messaging, which is free.

No Firebase file goes into the app. Everything is uploaded on the website, so you can turn push on or change the Firebase project without building the app again.

Step 1 — Create the Firebase project

  1. In the Firebase console, create a project.
  2. Add an Android app whose package name is your applicationId.
  3. For iPhones, add an iOS app with your bundle id.
Do the rebranding first. The package name in Firebase must match the app's id exactly. Registering com.shahinlabs.assetory and then changing the id means starting this step again.

Step 2 — Upload the files on the website

Open Settings → Push notifications and upload:

  • Service account key (.json) — Firebase → Project settings → Service accounts → Generate new private key. It is stored encrypted and never shown again.
  • google-services.json — Project settings → General → your Android app.
  • GoogleService-Info.plist — Project settings → General → your iOS app. Only needed for iPhones.

Turn on Send push notifications to phones and save. The status line changes to Ready: Firebase project ….

Step 3 — Test it

  1. Open the app, sign in, and allow notifications when asked.
  2. Back on the website, the page shows how many phones are registered. Press Send test notification.

iPhones also need

  • An APNs key — in Apple Developer → Keys, create a key with Apple Push Notifications service (APNs), then upload it in Firebase → Project settings → Cloud Messaging → Apple app configuration.
  • The capability — in Xcode (ios/Runner.xcworkspace) → Runner → Signing & Capabilities → + Capability → Push Notifications, then build the app again.
Who gets what is decided on the website. Settings → Notifications controls which roles receive each event; push simply delivers the same in-app notifications to the phone.

Release Builds

Version numbers, signing, and building for the Play Store and the App Store.

Once the app is connected and rebranded, it is ready to sign and build. Android can be built on any machine; iOS requires a Mac.

Step 1 — Set the version

The version is the version: line in pubspec.yaml, for example 1.0.0+1:

  • 1.0.0 — the version people see, and the one the website's Minimum and Latest version compare against.
  • +1 — the build number. Raise it for every upload: Google Play and the App Store both refuse a build number they have already had.

Step 2 — Create your Android keystore

Done once, and kept forever:

keytool -genkey -v -keystore release-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias release
Back up the .jks file and its passwords, and never share them. Google Play only accepts updates signed with the same key; replacing a lost one means asking Google to reset it.

Step 3 — Wire up the signing config

Create android/key.properties:

storePassword=…
keyPassword=…
keyAlias=release
storeFile=../release-keystore.jks

Both key.properties and the keystore are already in .gitignore.

Without this file, release builds fall back to the debug key, which Google Play refuses.

Step 4 — Build for Android

flutter build appbundle --release
flutter build apk --release --split-per-abi
OutputPath
Bundlebuild/app/outputs/bundle/release/app-release.aab
APK, per ABIbuild/app/outputs/flutter-apk/app-<abi>-release.apk

The bundle is what you upload to the Play Store. The APKs are for direct install and testing.

Use --split-per-abi for APKs. Without it Flutter packs every ABI into one large file. Hand testers the arm64-v8a APK, which any modern phone installs.

Step 5 — Build for iOS

Requires macOS with Xcode. Open ios/Runner.xcworkspace, go to Runner → Signing & Capabilities, and set your team. The bundle id must match the one in App Store Connect.

flutter build ipa --release

Step 6 — After the stores accept it

Once the new version is live, return to the website's Settings → Mobile app and raise the Latest version — and the Minimum version too, if older builds must stop working.

Modifying the Source

How the project is laid out, code generation, translations and tests.

For buyers who want to edit the app. The project is feature-first: each screen owns its pages, widgets, providers and repository.

lib/
  core/        config, errors, translations, media, push, session, storage
  models/      data types shared across screens
  network/     the Dio client and its interceptors
  router/      go_router routes and the bottom bar shell
  screens/     one folder per feature
  theme/       colours, accent palette, typography
  utils/       formatting helpers
  widgets/     shared dialogs, fields and list tiles

Each feature folder under screens/ holds pages/ for the routes, widgets/ for the pieces those pages are built from, providers/ for Riverpod state and repository/ for the code that calls the API.

Before you change anything

The server decides what a user may see

Roles, prices and record privacy are all enforced by the website's API. The app hides buttons a user cannot use, but never relies on that for security — a change to who sees what belongs on the server, in app/Support/RecordAccess.php and the role permissions.

Errors never reach the user raw

Network and server errors are mapped to readable messages — No connection, Your session ended, You don't have access to this — in lib/core/errors/. Add new wording there, not at the call site.

Generated code

Models and providers use code generation. After editing them, re-run the generator, or the build fails on stale generated files:

dart run build_runner build --delete-conflicting-outputs

Translations

The app ships in six languages: English, Arabic, Spanish, French, Portuguese and Turkish. The text is in lib/core/l10n/arb/app_<code>.arb, with English as the template. After editing, regenerate:

flutter gen-l10n
Add every new key to app_en.arb first. It is the template; a key missing from it is not generated. Untranslated keys are listed in build/untranslated.json.

Tests

flutter test
flutter analyze

Two tests talk to a real server, test/live_api_smoke_test.dart and test/end_to_end_live_test.dart. They are skipped unless you give them one:

flutter test test/live_api_smoke_test.dart \
  --dart-define=LIVE_URL=https://your-server.example.com \
  --dart-define=LIVE_EMAIL=admin@example.com \
  --dart-define=LIVE_PASSWORD=your-password \
  --dart-define=LIVE_API_KEY=your-api-key
Use a test server for LIVE_WRITE=true. Adding --dart-define=LIVE_WRITE=true also runs the tests that create, edit and delete items, loans, maintenance, documents and subscriptions. They clean up after themselves, but a test item ends up in the recycle bin.

Packages

  • flutter_riverpod — state management
  • go_router — navigation
  • dio — the API client
  • flutter_secure_storage — the sign-in token
  • mobile_scanner — QR and barcode scanning
  • image_picker and flutter_image_compress — photos
  • firebase_messaging — push notifications
  • fl_chart — the home screen charts
  • flutter_launcher_icons and flutter_native_splash — icon and startup screen

Web Manual

How to use the Assetory website day to day — every screen in the app, in the order you will meet them.

This section explains how to use the Assetory website once it is installed. The sidebar is arranged in five groups — Overview, Inventory, Records, Insights and Management — and the pages below follow it, so you can read along with the menu in front of you.

Menus hide themselves. Each menu entry needs a permission, and an entry a role cannot use is not shown at all rather than shown and blocked. Two people can therefore see very different sidebars. If a page described here is missing for someone, check their role first.

Roles

Assetory ships with three roles. You can add your own under Settings → Roles.

  • Admin — full access, including settings, roles and webhooks. Protected: it cannot be edited or deleted.
  • Member — adds and edits items, lending, maintenance, documents and subscriptions, and sees prices. No deleting and no settings.
  • Viewer — looks only, and never sees prices.

Who sees what

On top of the role, a record that belongs to a person is private to them:

  • An item assigned or lent to someone is seen only by that person, the user who added it, and the user who handed it out. Items nobody holds are shared. Their lending, maintenance and disposal records follow them.
  • A document linked to a person, such as a passport, is theirs. A subscription with people on it is theirs. Without a person, they follow their item or are shared.
  • Whoever added a record always sees it.
This holds everywhere — lists, exports, reports, label sheets, scans, the dashboard, reminders, the recycle bin, the audit log and the mobile app. A hidden record answers "not found" even when its address is typed in. The admin sees everything; give a role items.viewAll, documents.viewAll or subscriptions.viewAll to let it see everyone's records too.

Getting Started

First login, securing the admin account, and the order to set things up.

After installing, log in with admin@example.com / 12345678. You are asked to choose a new password before anything else opens.

Secure the admin account

  1. Click your name at the bottom of the sidebar and open Profile.
  2. Change the email to your own — the default address is public knowledge.
  3. Turn on Two-factor authentication, and save the recovery codes somewhere safe.

Recommended setup order

Some records depend on others existing first. This order avoids dead ends:

  1. Settings → General, Branding, Currency — site name, timezone, logo and how amounts are shown.
  2. Settings → Mail — so password resets and alerts can be sent.
  3. Settings → Categories — what kinds of things you own, with their code prefix, extra fields and depreciation.
  4. Locations — the places, rooms, cupboards and boxes things live in.
  5. Stores & services — where you buy things and who repairs them.
  6. People — your family or team, with logins for those who should sign in.
  7. Items — add them one by one, or bring them in with Import.
  8. Labels — print QR labels and stick them on.
Tip: you do not have to finish everything up front. The item form lets you add a missing store or tag on the spot, but setting up categories and locations first means codes and filters work from the first item.
Check System health once. Management → System health shows whether cron, email, HTTPS and the storage link are working. A red badge on the menu means something there needs attention.

Dashboard

The at-a-glance view of what you own and what needs attention.

The Dashboard is the landing page after login. It greets you and shows what needs your attention, limited to the records you are allowed to see.

What you see

  • Stat row — items, total value, lent out (with how many are overdue), in repair, and what is due in the next 30 days.
  • Needs attention — anything overdue or due within 30 days: warranties, loans, services, documents and renewals.
  • In repair — what is away, where, and since when.
  • Charts — items by category, location, status and condition; warranty cover; what is due by kind; items added and spending over the last 12 months.
  • Recently added — the newest items.
  • Subscriptions — the monthly and yearly cost of active subscriptions, and the next renewals.
  • What I have — the items you hold yourself, and when each is due back.
Viewers see no money. Without the prices permission, value cards and spending charts are left out rather than shown as zero.

Items

Adding, finding, moving, copying, exporting and importing the things you own.

Items is the heart of the app: everything you own or look after, each with a code, a place and a history.

Adding an item

Click Add item. Only the name, category and quantity are required; everything else can be added later.

  • Basics — name, category, location, condition and tags.
  • Details — brand, model, serial number, the product code (EAN/UPC barcode on the box), plus any extra fields the category defines.
  • Purchase & warranty — date, price, store, order number, and when the warranty ends.
  • Photos, receipts & files — up to 10 photos (the first is the cover), receipts, and manuals or certificates.
The code never changes. Each item gets a code made from its category's prefix, such as LAP-00012. It is printed on the QR label, so it stays the same even if the item is renamed or recategorised.

Status and condition

The status — Available, In use, Lent out, In repair, Lost or Disposed — is mostly set for you by lending, maintenance and disposal. The condition — New, Good, Fair, Poor or Broken — is yours to keep up to date.

Finding items

Search by name, code, serial number or brand, and filter by category, location (with or without sub-locations), tag, warranty, condition, store, status or holder. The Active tab hides disposed and lost items; Archived shows them.

The item page

The Overview tab shows every detail with its photos, the cost (purchase price plus maintenance), depreciation, lending, maintenance, documents and subscriptions. The History tab lists everything that ever happened to it — added, moved, lent, repaired, disposed — with who did it.

Actions

  • Move — to another location, with an optional note that lands in the history.
  • Make a copy — a new item with a new code and the same details and tags, without the serial number or files.
  • Move to recycle bin — it can be restored later.

Exporting

Export downloads the list with the current filters as Excel, CSV or PDF.

Importing

Import adds or updates many items at once from an Excel or CSV file of up to 1,000 rows and 5 MB.

  1. Click Download template. It has the right headings, an example row, and a second sheet listing your categories, locations and custom fields.
  2. Fill it in and choose the file.
  3. Decide whether to Update existing items and whether to Create missing categories, locations and stores.
  4. Click Check file. A preview shows what each row will do — New, Update, Skip or Error — with the reason.
  5. Nothing is saved until you click Import.
Rows are matched by item code. A row whose code already exists updates that item, and only the columns present in the file change. Categories and locations are matched by name or by full path, like Building › Floor › Room.

Locations & Labels

Places, rooms, boxes and vehicles, and printing QR labels for them and for items.

Locations describe where things are. A location can be a Place, Room, Storage, Box / bag, Vehicle or Other.

Nesting

Locations nest up to five levels deep — for example Home › Garage › Shelf A › Box 3. Use Add inside on any location to create one within it, and Show the tree to see the whole structure.

Each location page lists what is inside it and the items stored there, with or without its sub-locations.

A location can only be deleted when nothing is stored inside it — no items and no sub-locations. Its code is never reused, so an old printed label can never point at the wrong place.
Tip: set a location inactive instead of deleting it to hide it from pickers while keeping its history.

Printing labels

Every item and location has a QR code that links to it. Scanned with the mobile app — or any phone camera — it opens that record, after signing in if needed.

  1. Tick the items you want on the Items list, or open a location and choose Print labels for everything inside.
  2. Click Print labels and pick a label sheet.
  3. For a half-used sheet, set Skip first labels so printing starts at the first empty one.
  4. Click Print to get a PDF ready for the printer.

The sheets available are A4 with 24 or 65 labels, US Letter with 30, and label-printer rolls of 50 × 25 mm and 62 × 29 mm (Brother DK-11209).

Label and code settings

Settings → Items & labels sets the item and location code prefixes, the number of digits, the default label sheet, and what each label shows: the site name, the item or location name, the location, and the logo. A live preview shows the result.

Code changes apply to new items only. A category with its own prefix, such as LAP, uses that instead of the general item prefix.

Lending

Assigning items to people and lending them out, returns, due dates and overdue loans.

Lending answers "who has what". An item can go to a person in two ways:

  • Assign — it stays with them, like a work laptop. No due date needed.
  • Lend — it must come back by a date, which then shows in Upcoming.

Lending or assigning an item

  1. Open the item and click Lend / Assign.
  2. Choose the person. Someone not yet in People can be added on the spot with Add contact.
  3. For a loan, set the due date — the quick buttons add a day, three days, a week or two weeks.
  4. Optionally note the condition, add notes such as "with 2 batteries and the charger", and attach up to 5 photos.
An item can't be lent while it is in repair, lost or disposed. Bring it back from repair, or mark it found, first.

The Lending page

Four tabs — Out now, Overdue, Returned and All — filtered by kind, person and date. From each row you can return the item, extend the due date or edit the notes.

Returns

Return records when it came back and its condition now, with notes and photos — "small scratch on the lid". The person's own page also has Return all, which brings everything they hold back in one step with the same condition and notes.

Overdue loans chase themselves. Once a due date passes, the loan moves to Overdue and alerts repeat as set in Settings → Upcoming — as long as cron is running.

Maintenance

The service log, repairs away from home, repeating services and service types.

Maintenance keeps the service log, tracks repairs, and reminds you of repeating services. It has three tabs: Service log, Schedules and In repair now.

Logging a service

Click Add → Log a service (or Log a service on the item), pick the item, and record the type, title, date, who did it, the cost, notes, receipts and photos. The cost is added to the item's total spent.

Sending an item for repair

Turn on Item is away for repair when logging. Then:

  • the item shows In repair and appears on the In repair now tab and the dashboard;
  • it cannot be lent meanwhile;
  • you can note when it is Expected back.

When it returns, click Back from repair, enter the date and the final cost, and it becomes available again.

Repeating services

Add repeating service creates a schedule — "AC service every 6 months". It shows in Upcoming on time. Mark done logs the service and sets the next date.

The next date counts from the day the service was really done, not from when it was due. A service done two weeks late moves every later date along with it.

Set Upcoming days before on a schedule to override the global lead time, or turn it off with Active to pause it without deleting it.

Service types

Settings → Service types holds the kinds of service used in the log, such as Calibration or Oil change, each with an optional icon. A type still used by logs or schedules can't be deleted — set it inactive instead.

Inventory Audits

Counting a location to check items are really where the app says, and acting on the result.

An inventory audit checks that items are really where the app says they are: you scan or tick what you find, and the audit shows what is missing.

Starting an audit

  1. Click Start audit.
  2. Give it a name, such as "Office count, October 2026".
  3. Choose the location — its sub-locations are included — or Every location. Optionally limit it to one category.
The list of expected items is taken when the audit starts. Items added or moved afterwards do not change what the audit expects.

Counting

Type or scan a code into the box — an item code, a label link or a serial number. A USB or Bluetooth barcode scanner works here: it types the code and presses Enter, and the box stays ready for the next one. Or walk around with the mobile app and scan there.

Each item ends up in one of these states:

  • Found — where it should be.
  • Found elsewhere — found, but in another room than expected.
  • Missing — not found.
  • Extra — found here, but not on the list.

Items can also be marked found or missing by hand from the list.

Finishing

Finish audit marks every item still to check as missing. You can also tick Move items to where they were found, which updates their location and notes in their history that they were found during this audit.

A finished audit can't change any more. If you need to stop without marking anything missing, use Cancel audit — nothing moves, and it can still be read and exported.

Export downloads any audit to Excel.

Documents

Insurance, registration, passports and contracts with their expiry dates and renewals.

Documents are papers with an end date: insurance, registration, passports, warranties, leases and contracts. Upcoming follows each one's expiry date.

Adding a document

Click Add document and fill in the type, title, number, issuer, issue and expiry dates, cost or premium, notes, and the scans or photos.

Link it to an item (the car's insurance), a person (a passport), both, or neither for documents that belong to the whole household or team.

Status

Each document is Active, Expiring soon, Expired or has No expiry, and the tabs at the top filter by these.

Alerts

Alerts come Upcoming days before the expiry, then 7 days before, and on the day. The lead time is chosen in this order:

  1. the value on the document itself;
  2. the default of its document type;
  3. the document stages in Settings → Upcoming.

Renewing

Renew creates a new version with new dates. The old version stays in the document's History with its own files, so you can always find last year's policy.

Renew, don't edit, when a document is replaced. Editing the dates overwrites the old version; renewing keeps it.

Document types

Settings → Document types holds the types — insurance, passport, lease — each with an icon and its own default Upcoming lead time.

Subscriptions

Licences and subscriptions — what they cost, when they renew, and their licence keys.

Subscriptions track anything you pay for again and again — streaming, phone plans, hosting, software — and licence keys.

Adding a subscription

Click Add subscription and fill in:

  • Name, category and provider.
  • Billing cycle — weekly, monthly, every 3 or 6 months, yearly, or one-time / lifetime — and the amount per cycle.
  • Started on and Next renewal, which is suggested from the start date and the cycle.
  • Renews automatically — when off, it ends after the renewal date unless you renew it by hand.
  • Seats and Used by — the people sharing it.
  • Licence key, account email, payment method, a manage URL, a linked device, notes and invoices.
The licence key is stored encrypted. Only people who manage subscriptions can reveal it, and every reveal is written to the audit log.
Payment method is a label only. Type something like "Visa ••4242" — never a full card number.

Cost summary

The top of the page shows the total per month, per year, spent this year, the next renewal, and the monthly cost by category.

Payments and renewals

Record payment logs a payment and moves the next renewal one cycle on. For auto-renewing subscriptions, turn on Record automatic renewals in Settings → Upcoming and the payment is recorded for you once the renewal date passes, so the yearly spend stays right.

Cancelling

Cancel subscription takes it out of Upcoming; Reactivate brings it back.

A subscription with payments can't be deleted — cancel it instead, so the money already spent stays in your reports.

Categories, with their icons and colours, are managed in Settings → Subscription categories.

Stores & Services

Where you buy things, and who repairs, services or insures them.

Stores & services holds the businesses you deal with. Each is a Store (where things are bought), a Service provider (repairs, servicing, insurance) or Store & service (both, like a brand store with a repair desk).

Each record holds a contact person, email, phone, website, address and notes. Those contact details then show up next to every item bought or serviced there.

The vendor page

Opening a store or service shows what was bought here and the services it carried out, with the total spent.

Adding one on the spot

The item, maintenance and document forms let you add a new store or service provider by name without leaving the form. Fill in the contact details later.

A vendor still linked to items, documents, maintenance logs or subscriptions can't be deleted. Set it inactive to hide it from pickers instead.

Upcoming

Every warranty, loan, service and renewal by date, and when alerts go out.

Upcoming gathers every date that matters into one list: warranties ending, documents expiring, subscriptions renewing, services due and loans due back.

The list

Entries are grouped into Overdue, Today, Next 7 days and Later. Filter by kind, and choose how far ahead to look — 7, 30 or 90 days, overdue only, or all dates.

Snooze and dismiss

  • Snooze hides an entry until a later date — tomorrow, in 3 days, next week, next month, or a date you pick.
  • Dismiss hides it for good. Show dismissed lists them again, and Restore brings one back.
Upcoming follows its source. You never add entries here by hand. Change the warranty date, the due date or the renewal date on the item, loan or subscription, and the entry moves with it.

Alert settings

Settings → Upcoming controls when alerts go out:

  • Send alerts from — the hour, in the site's time zone. Alerts go out once a day from then.
  • How early — for each kind, up to four stages, such as 30 days before, 7 days before and on the day.
  • Repeat overdue alerts — only once, every day, every 3 days, weekly or every 2 weeks.
  • Record automatic renewals — see Subscriptions.
Alerts need the cron job. The settings page shows when the alerts job last ran. If it says it has never run, check cron on System health.

Disposing of Items

Selling, giving away or writing off items, marking them lost or stolen, and undoing it.

When an item leaves for good — or goes missing — use Dispose… on the item page. It leaves the active lists and Upcoming, while its history, documents and photos stay.

Reasons

  • Gone for good — Sold, Given away, Recycled, Thrown away or Written off.
  • Missing — Lost or Stolen.

Record the date, who it went to (or who it was reported to), a reference such as a receipt or police report number, the sale price for a sale, notes and files. The value at the time and the gain or loss are worked out for you.

An item that is with someone must be returned first. The exception is Lost or Stolen, which closes the loan for you.

Found again

A lost or stolen item has Mark as found. It becomes available again, optionally at a new location.

Repeating services stay off after an item is found until you turn them back on, so nothing is suddenly overdue.

Undoing a disposal

Undo disposal makes the item available again. The disposal stays in its history, marked as undone.

Disposed and lost items are listed on the Archived tab of Items and in the Disposed & lost report.

Reports & Depreciation

The eight built-in reports, exporting them, and the book value of what you own.

Reports are ready-made, with filters. Preview on screen, then download for Excel, as CSV or as a PDF.

Available reports

  • Inventory for insurance — a photo inventory with prices and receipts, grouped by place. Send it to your insurer before a renewal, or after a theft, fire or flood.
  • Inventory list — everything with its details, for one place, a category or one person.
  • Value summary — total purchase cost and current value by category or by location.
  • Expiring soon — warranties, documents and subscriptions ending in the coming days.
  • Lending — what is out, what is overdue, and the lending history for a period.
  • Maintenance costs — what was spent on repairs and service, per item and per type.
  • Subscriptions — active subscriptions with their monthly and yearly cost, and the payments in a period.
  • Disposed & lost — what was sold, given away, recycled or lost, with the gain or loss.
Money columns follow the prices permission. Without it, price and value columns are dropped from the report and its downloads — the rest of the report still works.

Depreciation

Set depreciation on a category (Settings → Categories → Edit), and its items get their current value worked out from the purchase price and date:

  • Straight line — the same amount every year until the salvage value.
  • Declining balance — double-declining: loses most in the first years, then less and less.
  • Sum of the years — loses more in the first years, in steps that shrink each year.

Add a useful life in years and a salvage value as a percentage of the price. Sub-categories inherit their parent's method unless set otherwise. Values are recalculated every night and whenever the item or category changes.

The Insights → Depreciation page shows the purchase price, what has been lost so far, the book value and this year's loss — overall, per category, per year (the last five and the next five), and the items losing the most.

Items without a purchase price or date are not counted. The page tells you how many there are, so you can fill them in.

People & Logins

Family or team members and outside contacts, and giving members a login.

People holds everyone things are assigned or lent to. There are two kinds:

  • Member — part of your family or team. Can be given a login.
  • Contact — someone outside, like a neighbour or a client. Never signs in.

Each person has a relationship ("Daughter", "Client"), email, phone, photo and notes. Their page shows what they hold now and their whole lending history, with Return all for everything they hold.

Giving a member a login

  1. Open the member and click Give login.
  2. Enter their email, choose a role, and set a password — or press Generate.
  3. Leave Must choose a new password at first sign-in on.
  4. Optionally tick Email them that the account was created. The password itself is never sent.

The login works on both the website and the mobile app.

Turn off login signs the person out on the web and in the app straight away, and they can no longer sign in. Their records and history stay.
Tip: set a person inactive rather than deleting them to hide them from pickers while keeping who had what.

Audit Log, Recycle Bin & System Health

Who changed what, restoring deleted records, and checking the server is healthy.

Audit Log

The audit log records who changed what and when across the app. Filter by resource, event and date, and open View changes to see each field's old and new value. Use it to answer questions after the fact — who moved an item, who revealed a licence key, why a value changed.

Recycle Bin

Deleted records stay in the Recycle Bin until you restore them or delete them forever. It holds items, people, locations, vendors, documents, subscriptions and their payments, maintenance logs and schedules, categories and their fields, tags, types and languages.

  • Restore puts a record back where it was, with its links and files.
  • Delete forever removes it together with its files. For an item, its photos, receipts and history go too.
Delete forever cannot be undone. Records in the bin are never removed on their own, so restoring stays possible until someone chooses to delete them.

System health

One page that checks everything the site depends on, with a fix for each problem:

  • Cron job — when it last ran. Once a minute is best.
  • Background jobs — jobs waiting or failed, in VPS mode.
  • Upcoming alerts — when they were last sent.
  • Email — whether a real mail server is set up.
  • Site address — whether APP_URL matches the address in use. QR labels and email links use it.
  • HTTPS — the mobile app needs it.
  • Debug mode — must be off on a live site.
  • Storage link — with a Fix it now button.
  • Folder permissions, Disk space, PHP and the app Version.
A badge on the menu means a problem. If cron stops, admins also see a banner at the top of the site pointing here.

Webhooks

Sending signed events to Slack, Zapier, n8n or your own system when something happens.

Webhooks send a signed POST with a JSON body to another app when something happens here — an item is created, lent, returned, repaired or disposed, a person or subscription changes, or something is due soon or overdue. Only the admin role can manage them.

Adding a webhook

  1. Click Add webhook.
  2. Give it a name and the Endpoint URL. It must be a public internet address.
  3. Tick the events to send, grouped by items, lending, maintenance, documents, disposals, people, subscriptions, audits and Upcoming.
  4. Save, then press Send test.

Request format

The body is { "id", "event", "created_at", "data" }, and every request carries these headers:

HeaderValue
X-Webhook-EventThe event, e.g. item.lent
X-Webhook-DeliveryThe delivery id — the same on every retry
X-Webhook-TimestampUnix time of this attempt
X-Webhook-Signaturesha256= + HMAC-SHA256 of "<timestamp>.<raw body>" with the webhook's secret

The webhook page shows the signing secret and ready-made PHP and Node.js samples for checking the signature.

Deliveries and retries

Answer with any 2xx within 10 seconds. A failed send is retried after 1 min, 5 min, 30 min, 2 h and 12 h; after 10 failures in a row the webhook turns itself off. The page lists every delivery of the last 30 days, with the request and the answer, and can resend any of them.

Optional .env settings

  • WEBHOOKS_HEADER_PREFIX=X-Assetory — puts your own brand in the header names: X-Assetory-Event, X-Assetory-Signature and so on. Use letters, digits and dashes.
  • WEBHOOKS_ALLOW_PRIVATE_HOSTS=true — allows addresses on your own network, such as an n8n server on 192.168.x.x.
Set the header prefix once, before anyone connects. Receivers check these names, so changing it later breaks their signature check. For the same reason, renaming the site in Settings never changes them. Run php artisan config:clear after editing .env if you cache config.
New secret takes effect immediately. Requests signed with the old secret fail the receiver's check, so update the receiver at the same time.

Profile & Notifications

Your own account, two-factor authentication, language, and the notification inbox.

Profile

Open Profile from your name at the bottom of the sidebar.

  • Profile information — name, email, phone and avatar (PNG, JPG or WEBP, up to 2 MB).
  • Change password — at least 8 characters with upper and lower case, a number and a symbol. Every other session is signed out.
  • Language — each person chooses their own.

Two-factor authentication

  1. Click Enable 2FA and confirm your password.
  2. Scan the QR code with an authenticator app, or enter the setup key by hand.
  3. Enter the 6-digit code to confirm.
  4. Save the recovery codes in a password manager.
Each recovery code works once. They are your way in if you lose your phone. View them again later with your password, or Regenerate codes to replace them all.

Notifications

The bell at the top shows unread notifications. View all notifications opens the inbox, where you can filter by status and group, mark them read, and delete them.

Admins also have Broadcast, which sends a notification to every active user on the installation.

Notifications appear instantly only with Pusher. Without it they still arrive, just not in real time. Phones get them as push notifications once Firebase is set up — see Mobile Installation → Push Notifications.

Settings

App-wide configuration — roles and permissions, mail, server, notifications, languages and the mobile app.

Settings is app-wide configuration, arranged in tabs. The system tabs — Server, Mail, Push notifications, Pusher and the mobile API key — are admin-only.

Site

  • General — site name, timezone, date and time format.
  • Branding — logos for light and dark mode, and the favicon. Used across the app, emails and reports.
  • Theme — the primary colour, with a live preview.
  • Currency — code, symbol, position, decimal places and separators.

Catalogue

  • Categories — two levels: categories and sub-categories, each with an icon, colour, code prefix (2–6 letters or digits, e.g. LAP) and depreciation. Fields adds extra details, such as RAM for laptops or the plate number for cars — text, long text, number, date, choice list, yes/no or link, optionally required.
  • Tags — free labels that cut across categories and locations, like Gift or Insured. Members can also create tags while editing an item.
  • Items & labels, Service types, Document types, Subscription categories and Upcoming — covered on their own pages above.
A field's key can't change once items store values under it. Deleting a field keeps the saved values, but stops showing them.

Roles and permissions

Roles lists every role with its user count. Permissions lets you pick a role and toggle what it can do; changes apply when you click Save. The admin role is protected and always has everything.

Notifications

  • Templates — edit the subject and body of each event's message, with the variables you can use.
  • Routing — for each event, which roles receive it in-app and by email.
  • Per-user events — events sent to one specific person, such as an item assigned to them, switched on or off per channel.

System

  • Server — Shared hosting or VPS / dedicated, which decides how background jobs run, and Force HTTPS.
  • Mail — SMTP host, port, username, password, encryption and the from address, with a test email. The Log driver writes emails to the log file instead of sending them.
  • Push notifications — Firebase files for phone notifications.
  • Pusher — real-time in-app notifications without refreshing.
  • Cache — clears the application, settings, config, route and view caches. No data is touched.
Choose VPS only on a VPS. In VPS mode, emails and notifications wait for the cron job to work the queue. On shared hosting that means they are never sent.

Languages

The app ships in English, Arabic, Spanish, French, Portuguese and Turkish, with right-to-left support. Add language creates a new one as a copy of the English text. Then translate it with the Translate button, file by file, or edit its files on the server in public/lang/<code>/ (screens) and lang/<code>/ (emails and messages). Changes show on the next page load, with no build.

Mobile app

The server address and API key the app is built with, the minimum and latest app versions, and the store links. See Mobile Installation → Connecting to Your Server.


Mobile Manual

How to use the Assetory app day to day — every screen in the app, in the order you will meet them.

This section explains how to use the Assetory app. The bar along the bottom has five places — Home, Items, the Scan button in the middle, Upcoming and More — and the pages below follow the order you will actually meet them in: sign in, look around, scan, then work with items.

The app works with your website, not on its own. Everything you see and change is stored on the server, so the website shows it straight away and the app needs a connection. When there is none, a You're offline banner appears, and anything you try asks you to connect and try again.

The five places

  • Home — the overview and what needs attention.
  • Items — everything you own, searchable and filterable.
  • Scan — the camera, for QR labels and barcodes.
  • Upcoming — every due date, in order.
  • More — lending, locations, people, maintenance, inventory audits, documents, subscriptions, notifications and your profile.
You see what your role allows. The app uses the same login, role and privacy rules as the website, so sections a role cannot open are simply not shown under More, and a viewer never sees prices.

Signing In

Signing in, two-factor codes, the first-time password, and the screens shown before sign-in.

Sign in with the same email and password as on the website. Accounts are created on the website — see Web Manual → People & Logins.

Two-factor authentication

If the account has two-factor turned on, the app asks for the 6-digit code from your authenticator app. Lost the phone? Tap Use a recovery code instead and enter one of your recovery codes.

Choosing a new password

A new login, or one the admin reset, must choose its own password before anything else opens. It needs at least 8 characters, upper and lower case letters, a number and a symbol; the screen ticks off each rule as you type.

Changing your password signs you out everywhere else. Other phones and browser sessions have to sign in again with the new one.

Screens you may see before sign-in

  • This app isn't set up yet — the build still has the sample server address. It needs rebuilding; see Mobile Installation → Connecting to Your Server.
  • This server address isn't secure — the build uses http:// for an internet server. It must be https://.
  • Can't connect right now — the API key does not match the server's, usually because a new key was generated on the website.
  • Update required — this version is older than the minimum the website allows. Open store takes you to the update.
You stay signed in. The sign-in is kept on the phone until you sign out, your login is turned off, or the app goes unused for a long time.

Home

The overview card, what needs attention, your own items and the insight charts.

Home opens first after sign-in. It is a short version of the website's dashboard, built for a quick look.

What is on screen

  • Overview card — total value and total items, with how many are lent out, overdue, in repair and due in 30 days.
  • Needs attention — overdue loans and anything due soon. Tap one to open it.
  • My items — what you hold yourself, and when each is due back.
  • In repair — what is away, and when it is expected back.
  • Insights — charts you can switch between: status, category, location, condition, warranty, due soon, items added and spending over the last 12 months.
  • Recently added — the newest items.
  • Subscriptions — the monthly and yearly cost, and the next renewals.

On a fresh install with no items, Home shows Add your first item and Scan a label instead.

Pull down to refresh. Home loads fresh figures each time you open it, and pulling down reloads them on the spot.

Scanning

Scanning QR labels and barcodes to open an item or location, or to start a new item.

The round Scan button in the middle of the bottom bar opens the camera. Point it at a label or barcode — it finds the code automatically, with nothing to press.

What a scan finds

  • An item's QR label — opens that item.
  • A location's QR label — opens that location, with everything stored there.
  • A serial number or the barcode on a product box — opens the item with that serial number or product code.
  • Several items with the same code — a list to pick from, for example two of the same product.

When nothing matches

The app says No item has this code and offers Add as new item. The new item form opens with the scanned code already filled in as the product code or serial number — the quickest way to add something still in its box.

Other ways in

  • Torch and Switch camera — for dark cupboards and awkward angles.
  • Type code — enter a code or serial number by hand when a label is damaged.
  • Upload from photos — read a code from a picture already on the phone.
  • Recent scans — jump back to something you scanned a moment ago.
Scanning needs a connection, because the code is looked up on the server. It also respects privacy: an item assigned to someone else, which you are not allowed to see, answers as if it did not exist.
Camera access refused? The scanner explains what is missing and offers Open settings to allow it — or type the code instead.

Items

Finding items, the item page and its actions, and adding or editing an item with the camera.

The Items tab lists everything you can see. Switch between a grid with photos and a compact list.

Finding items

Search by name, code or serial number. Filter by category, location (with or without sub-locations), status (Active, Archived or All), tag and warranty, and Sort by newest, name, code, purchase date, price or warranty end.

The item page

Photos at the top, then the details in sections: Details, Specifications (the category's extra fields), Purchase & warranty, Depreciation, Maintenance, Documents, Receipts & files and Notes. Who holds it, and since when, is shown near the top. See full history lists everything that ever happened to it.

Actions

The main button changes with the item's state — Lend / Assign, Return or Back from repair. More actions holds the rest:

  • Move — to another location, with an optional note.
  • Edit and Clone.
  • Add service and Add document.
  • Delete — moves it to the recycle bin on the website.

Adding or editing an item

Tap Add item. Only the name and category are required.

  • Photos — take them with the Camera or pick from the Gallery. They are compressed before upload.
  • Location — choose from the list, add a new one, or tap Scan box label to scan the QR label of the box it is going into.
  • Serial number — tap Scan beside the field to read it from a barcode sticker.
  • Category fields — the extra fields of the chosen category appear automatically.
  • Purchase and Warranty — with +1 year style shortcuts for the warranty end, and a Photo of receipt.
  • Tags — pick existing ones or type a new one.
Leaving with unsaved changes asks first. Back on a half-filled form offers to discard the changes, so a stray tap does not lose your work.

Lending & People

Lending and assigning items, returns with photos, and the people who hold them.

Lending or assigning

From an item, tap Lend / Assign:

  1. Choose Lend — comes back by a date — or Assign — they keep it.
  2. Pick the person. People you lent to recently are at the top, and New contact adds someone on the spot.
  3. For a loan, pick the due date or use the quick choices — 3 days, 1 week, 2 weeks.
  4. Add notes, such as "with 2 batteries and charger", and confirm.
An item in repair can't be lent. The app says so and points you to Back from repair first.

Returning

Tap Return on the item, or on the loan in More → Lending. Note its condition, and use Take photo of damage if something is wrong, then Mark as returned. Extend moves the due date instead.

The Lending screen

More → Lending has three tabs — Out now, Overdue and Returned — and can be filtered by person.

People

More → People lists Members and Contacts. A person's page shows what they are Holding, with buttons to call, message or email them, and:

  • Return all — brings back everything they hold in one step.
  • Lend something to this person — starts from the person instead of the item.
Logins are managed on the website. The app can add contacts, but giving a member a login or changing their role is done under People on the website.

Locations & Moving

Browsing locations, adding sub-locations and items to them, and moving items.

More → Locations lists your places, rooms, storage, boxes and vehicles. Open one to see what is Inside it and the Items inside, with or without its sub-locations.

From a location

  • Add item here — the new item form opens with this location already chosen.
  • Add sub-location — a room inside a building, a box on a shelf.

Moving an item

Open the item, tap More actions → Move, and pick the New location — or scan the location's label. Add an optional note, then Move here. The move is written to the item's history.

Scan the destination, not the item. When the app asks for a location, scanning an item's label instead tells you That's an item, scan a location label.
Tip: scanning a location's QR label from the Scan button opens the location itself — the fastest way to see what is in a box without opening it.

Maintenance

What is due, what is in repair, logging services and repeating schedules.

More → Maintenance has three parts: Due — services due in the next 30 days — In repair, and Recent services.

Logging a service

From an item, tap More actions → Add service. Choose the service type, give it a title ("Oil change"), the date, who did it — a Shop or a Person — the cost, notes and receipt photos.

Sending for repair

Turn on Item is going away for repair. The item shows In repair until you mark it back, and you can note when it is Expected back.

When it returns, tap Back from repair on the item, enter the date and the final cost.

A lent item can't go for repair. The app asks you to return it first.

Repeating services

The item's maintenance page lists its Repeating services. Add schedule sets one up — every so many days, weeks, months or years — and Mark done logs the service and sets the next date. Leave Upcoming days before empty to use the server's setting; turn Active off to pause it.

The same page shows the Total spent on the item and a chart of what was spent per year.

Inventory Audits

Walking a location with the phone and scanning what is really there.

The phone is the easiest way to count: walk around and scan the labels as you go.

Audits are started on the website. Choose the location and category there; the app then lists every audit in progress under More → Inventory audits.

Counting

  1. Open the audit. It shows the progress, such as 12 of 40 checked.
  2. Tap Scan items.
  3. Use Scan room to scan the label of the room you are standing in, so the app knows where you are counting at.
  4. Scan each item's label. The app answers straight away:
    • Found — where it should be.
    • Found in …, not where expected — it belongs in another room.
    • Not on the list, added as extra.
    • Already checked.

Back on the audit, the tabs — To check, Found, Elsewhere, Missing and Extra — show where everything stands. An item can also be marked found or missing by hand, or put back to to check.

Scan the room before its items. Without a room, every item you scan is recorded as found where it is expected, so nothing can show up as being in the wrong place.

Finish the audit on the website, where you can also move misplaced items to where they were found.

Documents & Subscriptions

Photographing documents, renewing them, and keeping track of subscriptions and licence keys.

Documents

More → Documents opens on Expiring, with tabs for All and Expired. Search by title or number, and filter by type.

  • Add document — tap Photograph document to capture it with the camera, then fill in the type, title, number, dates and the item or person it belongs to.
  • Renew — creates a new version with new dates. Older versions stay under Earlier versions, with their files.
  • The document number can be copied in one tap — handy at a counter or on a phone call.

Subscriptions

More → Subscriptions shows the cost per month, per year and spent this year, the next renewals, and the cost by category. Tabs filter Active, Cancelled, Ended and All.

  • Add subscription — with quick picks for common kinds such as domain, hosting and antivirus.
  • Record payment — logs a payment and moves the next renewal one cycle on.
  • Cancel subscription and Reactivate.
Licence keys hide themselves. Show reveals the key, which can then be copied, and it is hidden again after 30 seconds. Every reveal is recorded in the website's audit log.
The app refuses a card number. In Payment method, type a hint like "Visa ••4242". Something that looks like a full card number is rejected before it is saved.

Upcoming & Notifications

Every due date in one list, snoozing and dismissing, and the notification inbox.

Upcoming

The Upcoming tab lists warranties, documents, subscriptions, maintenance and loans by date, grouped into Overdue, Today, Next 7 days and Later.

  • Filter by kind, and choose the period — overdue only, 7, 30 or 90 days, or everything.
  • Snooze hides an entry until tomorrow, in 3 days, next week, next month or a date you pick.
  • Dismiss hides it for good; Undo appears for a moment if you change your mind. Show dismissed and Restore bring one back later.

Tap an entry to open what it is about — the item, the loan, the document or the subscription.

Notifications

More → Notifications is the same inbox as the website's bell, with All and Unread tabs and Mark all as read. Tapping a notification opens the record it is about.

Notifications on a closed app need push. Once the site has push notifications set up and you allowed them on the phone, alerts arrive even when the app is closed. Without push they are still in the inbox the next time you open the app.

Profile & Settings

Your account, password, language, theme and accent colour, and signing out.

More → Profile & settings holds everything about your own account and how the app looks.

My account

  • Edit profile — your name and phone.
  • Change password — signs you out on every other device.
  • Two-factor authentication — shows whether it is on.
Two-factor is turned on or off on the website, under Profile, because setting it up needs the QR code shown there.

App

  • Language — English, Arabic, Spanish, French, Portuguese or Turkish. Arabic switches the layout to right-to-left.
  • Theme — System, Light or Dark.
  • Accent color — Emerald, Ocean, Violet, Sunset or Rose, used for buttons, highlights and the home card.

Server and About

Server shows the site you are connected to and its version, with Open website. About shows the app version and the open-source Licenses.

Signing out

Sign out asks once, then removes the sign-in from this phone. The phone also stops receiving push notifications for your account.


Changelog

What changed in each release, and whether it affects a customised install or app build.

Every release of Assetory is listed here, newest first. Each entry ends with Affects customised builds — what to check if you have already rebranded the app or changed the source — so you can decide whether, and how, to update.

TagMeaning
[NEW]A feature that was not there before
[IMPROVED]Existing behaviour made better, or a library or toolchain upgraded
[FIXED]A bug fixed
[CHANGED]Behaviour that now works differently — read these before updating

v1.0.0 — October 2026

Initial release of the website and the Android and iOS app.

  • [NEW] Items with category-based codes, custom fields per category, photos, receipts, files, warranty tracking and a full history.
  • [NEW] Nested locations up to five levels, and printable QR labels on A4, US Letter and label-printer sheets.
  • [NEW] Lending and assigning, with due dates, overdue tracking and returns with condition and photos.
  • [NEW] Maintenance: service log, repairs away from home, and repeating service schedules.
  • [NEW] Inventory audits on the web or by scanning with the phone, with found, misplaced, missing and extra results.
  • [NEW] Documents with expiry dates and versioned renewals, and subscriptions with cost summaries, payments and encrypted licence keys.
  • [NEW] Upcoming: every warranty, loan, service, document and renewal by date, with scheduled email and in-app alerts.
  • [NEW] Eight built-in reports in Excel, CSV and PDF, and depreciation by straight line, declining balance or sum of the years.
  • [NEW] Excel and CSV item import with a preview, and exports of any filtered list.
  • [NEW] Roles and permissions, private records for assigned items, two-factor authentication, audit log and recycle bin.
  • [NEW] Signed webhooks with retries, push notifications through Firebase, and real-time notifications through Pusher.
  • [NEW] Web installer, System health page, and support for both VPS and shared hosting.
  • [NEW] Six languages — English, Arabic, Spanish, French, Portuguese and Turkish — with right-to-left layout and an in-app translation editor.
  • [NEW] Flutter app for Android and iOS: scanning, items, lending, maintenance, audits, documents, subscriptions, Upcoming and notifications, with light and dark themes and five accent colours.

Affects customised builds: not applicable — first release.

Updating a customised install

When a new version comes out, its entry says which areas changed. To bring those changes into your own copy:

  • Back up first. Take a copy of the database and of the whole site folder before uploading anything.
  • Keep what belongs to your install. Never overwrite .env or the storage/ folder — they hold your settings, keys, uploaded files and the installer lock.
  • Keep your copy under version control. If you changed the source, commit it, unzip the new release beside it, and compare the two folders so you can carry your changes across.
  • Check the database line. If an entry says the database changed, it lists the step that brings an existing database up to date.
  • Rebuild a customised front end. If you changed Vue files, run php artisan wayfinder:generate --with-form and npm run build after merging, and upload the new public/build/.
  • Clear the cache from Settings → Cache once the files are in place.

Updating a customised app

  • Keep your branding. lib/core/app_config.dart, your app id, the icons in assets/icon/, android/key.properties and your keystore are yours — do not copy them over from the new release.
  • Update the website first. A new app may rely on a newer server. Update the site, then build and publish the app.
  • Raise the build number in pubspec.yaml before uploading the update to the stores.
  • Raise the minimum version in the website's Settings → Mobile app only once the new build is live, if older builds must stop working.

License

What your CodeCanyon licence lets you do with the source code, in plain language, and the third-party licences that come with it.

Assetory is sold on CodeCanyon under the standard Envato Market licences. This page explains them in plain language for this product. If anything here differs from Envato's own wording, Envato's licence terms are what count.

One purchase, one end product

Each licence covers one end product — one installation of the website on one domain, together with the mobile app built for that website, by you or for one client. You can update it as often as you like, for as long as you like.

Running two or more installations — for different companies, clients or domains — needs one licence for each. A second copy of the website for another client, or a second app published under another app id for another server, counts as another end product.

Regular or Extended?

How it is usedLicence needed
Your own company, team or household tracks its assets, and nobody pays to use itRegular
You build and install it for a client, who uses it internallyRegular
People pay you to sign in or use it — a hosted service, paid accounts or a subscriptionExtended
The mobile app is paid to download, or unlocks features with in-app purchasesExtended

The rule behind the table: if the people using it have to pay to get it, or to use any part of it, you need the Extended licence.

What you can do

  • Change anything in the source — the name, logo, colours, text, features and code, on the website and in the app.
  • Publish the app on Google Play and the App Store under your own name and app id.
  • Install and customise it for a client and charge for your work. You are buying the licence on your client's behalf, and the finished product is theirs.
  • Keep using the source after your support period ends. The licence does not expire.

What you cannot do

  • Share or resell the source code — as-is, modified, as a template, or as part of another source-code product, free or paid. This applies under both licences.
  • Run more installations than you have licences for.
  • Pass the licence on to someone else, except to the client you built the product for.

Attribution

No attribution to us is required. You do not need to mention Assetory, ShahinLabs or CodeCanyon anywhere on your site, in your app or in its store listing. The site name and branding settings, and the app's rebranding steps, replace every visible mention of the Assetory name.

Third-party libraries

Assetory is built on open-source and third-party libraries, each under its own licence. Most of them allow commercial use with no conditions that affect you:

  • Website, PHP — Laravel and almost every PHP package, including the Spatie packages, Fortify, Sanctum and Laravel Excel, are under the MIT or BSD licences.
  • Website, front end — Vue, Inertia, Tailwind CSS, Reka UI, Chart.js and vue-i18n are MIT; the Lucide icons are ISC.
  • Mobile app — Flutter and the app's packages, including Riverpod, go_router, Dio, mobile_scanner, fl_chart and the Firebase plugins, are under the MIT, BSD or Apache 2.0 licences.

One library comes with terms you should read before you publish:

mPDF — PDF exports

PDF reports, exports and label sheets are made with mPDF, which is licensed under the GNU GPL v2; its QR code library, mpdf/qrcode, is under the GNU LGPL v2.1. Both ship unmodified in vendor/mpdf/ with their licence files.

Not sure which licence fits your plan? See Envato's licensing FAQ, or contact us through the Support tab on the item page before you publish. For a third-party library, its own licence file is what counts.

AI Disclosure

How AI tools were used to make Assetory.

AI tools helped write parts of the code, the documentation and the item description, and the Assetory logo was made with an AI image tool. The author reviewed and tested everything, and the screenshots show the real app.

Assetory has no AI features: nothing you store in it is sent to an AI service.