T3Vault 

Extension key

t3vault

Package name

codemacher/t3vault

Version

main

Language

en

Author

codemacher UG

License

This document is published under the Creative Commons BY 4.0 license.

Rendered

Mon, 07 Sep 2026 11:40:53 +0000


T3Vault is a backup manager for TYPO3. It provides an administrator backend module and a standalone recovery application that can create encrypted backups, restore them on another server (including Composer ↔ classic/legacy layout conversion), and prove integrity with OpenTimestamps time stamps on the Bitcoin blockchain — so later changes to a stored backup can be detected.

Install via Composer (composer require codemacher/t3vault) or from the TYPO3 Extension Repository. See Installation for provisioning and first-time setup.


Table of Contents:

Introduction 

What does it do? 

T3Vault creates encrypted backups of a TYPO3 installation (files and database) and helps you restore them — including on a different server after a disaster. The package is published as codemacher/t3vault (extension key t3vault).

The extension ships two layers:

  1. A TYPO3 backend module (admin-only) that embeds the T3Vault UI and can set the standalone password.
  2. A standalone application under <web-dir>/t3vault that also runs without a working TYPO3 instance (disaster recovery).
T3Vault dashboard with backup statistics, quick actions and recent jobs

Dashboard after login: backup stats, quick actions and recent jobs.

After each completed backup, T3Vault seals integrity data so later modifications can be detected:

  • SHA-256 hashes of backup artefacts in integrity.json
  • optional HMAC bound to the site secret
  • an OpenTimestamps proof (integrity.json.ots) that time-stamps the seal on the Bitcoin blockchain via public calendars — independent of TYPO3, the hosting provider, or whoever stores the archive (no payment or currency transfer)

On restore, the installer can convert the TYPO3 layout:

  • Composer → classic/legacy (document-root layout) when the target has no Composer workflow
  • Classic → Composer (e.g. for DDEV or modern project structure)

Features 

  • OpenTimestamps integrity seals time-stamped on the Bitcoin blockchain to detect tampering after the backup was created
  • Layout conversion on restore (Composer ↔ classic/legacy)
  • Full and fileadmin-only backups (Composer and classic TYPO3 layouts)
  • AES-256 encrypted ZIP parts (shared backup password)
  • Database dump (database.zip, AES-256) with mysqldump or PHP fallback
  • Downloadable .t3vault.tar archives plus installer / DDEV setup script
  • Browser-independent backup processing (server-side self-invocation)
  • Integrity verification in the UI, via CLI / PHAR, or with the official OpenTimestamps client
  • HTTP proxy support for calendar traffic

Target groups 

  • Administrators who need reliable backups and offline restore
  • Agencies / hosting teams moving sites between environments — including Composer sites that must land as classic/legacy on the target
  • Security-conscious operators who want an independent OpenTimestamps time stamp (Bitcoin blockchain) that a backup was not altered after sealing

Compatibility 

PHP 8.2 – 8.5
TYPO3 13.4 – 14.x
License GPL-2.0-or-later

Installation 

Composer 

The recommended install path is Composer. Require the package in your TYPO3 project (also on Packagist and TER):

composer require codemacher/t3vault
Copied!

T3Vault depends on codemacher/elephstamp (OpenTimestamps), which is pulled in automatically from Packagist.

Provisioning of <web-dir>/t3vault runs via typo3/cms-composer-installers (InstallerScripts) on composer install / update / dump-autoload.

Standalone app provisioning 

On every composer install / update / dump-autoload, T3Vault copies the standalone app into <web-dir>/t3vault.

  • The configuration (<project>/var/t3vault/config.local.php and backup.password.php, outside the web root) is never overwritten; legacy copies inside <web-dir>/t3vault/ are migrated automatically.
  • Opening the T3Vault backend module also re-syncs that folder as a fallback.

After composer remove codemacher/t3vault, delete the leftover web copy manually if it is still present:

rm -rf public/t3vault
Copied!

Activation 

  1. Activate the extension in the Extension Manager / Package Manager (Composer installs usually activate automatically).
  2. Open Admin Tools → T3Vault (module path /module/tools/t3vault; admin users only).
  3. Set the standalone password when prompted (used for disaster recovery login outside the TYPO3 backend).
  4. In the T3Vault UI settings, set a backup encryption password (minimum 8 characters) before creating the first backup.

Web root hardening 

The standalone app is provisioned into the public web root (<web-dir>/t3vault). The configuration with all secrets lives outside the web root (<project>/var/t3vault/, see Configuration), and so does the ENABLE_T3VAULT marker (see Enable marker); the app directory still ships an Apache .htaccess that protects legacy config.local.php copies, legacy marker files, the installer templates (*.tpl) and any backup artifacts, and restricts the Api/ directory to Api/index.php.

nginx equivalent (place inside the relevant server block):

location ^~ /t3vault/ {
    # Protect legacy configuration/marker copies inside the web root.
    location ~ ^/t3vault/(config\.local\.php|ENABLE_T3VAULT)$ { deny all; }

    # The Api/ directory only exposes index.php – everything else
    # (framework classes, installer templates) is read from disk by PHP.
    location ~ ^/t3vault/Api/(?!index\.php)  { deny all; }

    # Never serve templates, logs, PHARs or backup artifacts as static files.
    location ~ ^/t3vault/.*\.(tpl|log|sql|gz|zip|tar|phar)$ { deny all; }

    # Security headers for the SPA shell (T3V-022). frame-ancestors does NOT
    # fall back to default-src and must be set explicitly; 'self' allows the
    # same-origin TYPO3 backend iframe. script-src is 'self' only (the shell
    # loads bootstrap.js instead of an inline block); style-src keeps
    # 'unsafe-inline' for MUI/Emotion's runtime styles.
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'self'" always;
    add_header Referrer-Policy "no-referrer" always;
    add_header X-Content-Type-Options "nosniff" always;
}
Copied!

For classic installations (project root equals the document root) the private runtime directory var/t3vault/ (configuration, secrets, state) is web-reachable and must be denied as well:

location ^~ /var/t3vault/ { deny all; }
Copied!

Additionally protect the backup target directory. The default (<project-root>/var/t3vault/backups) already lives outside public/; if you override backupBaseDir, keep it outside the web root.

Configuration 

Runtime settings live outside the web root in <project>/var/t3vault/config.local.php (0600). The plaintext backup password is stored separately in <project>/var/t3vault/backup.password.php (0600) so a single file read never yields both the signing secret and the archive password. Provisioning never overwrites these files; legacy copies inside <web-dir>/t3vault/ are migrated automatically. Settings can also be changed from the T3Vault UI (Settings).

T3Vault Settings dialog with data directory, passwords and HTTP proxy

Runtime settings can be edited in the UI; secrets stay outside the web root.

Settings overview 

Key Description
passwordHash Argon2id hash of the standalone login password
secret Signing secret (sessions, SSO tokens, integrity HMAC)
backupPassword AES-256 password for all backup ZIP archives (min. 8 chars); stored in backup.password.php, not in the config file
backupBaseDir Absolute override for the backup directory; empty = default
allowedBackupRoots Additional roots a backup directory may live in (project root is always allowed)
uploadMaxFiles Files accepted per import request (default 200)
uploadMaxFileBytes Maximum size of a single imported file (default 20 GiB)
uploadMaxTotalBytes Maximum total size of one import (default 40 GiB)
httpProxyMode Proxy mode for OpenTimestamps calendar traffic
httpProxy Custom proxy URL when httpProxyMode is custom
canonicalHost Host (optionally host:port) used for self-generated URLs; empty = validated request host
trustedHostsPattern Anchored regex the request Host must match to be trusted (mirrors the TYPO3 setting of the same name)
trustedProxies List of reverse proxy IPs whose X-Forwarded-Proto is believed; empty = ignore forwarded headers
allowedOrigins Explicit cross-origin origins; empty = same-origin only (no wildcard is accepted)

Scheduled backup tasks are not stored in config.local.php. They live in tasks.json next to the config (same data directory, mode 0600). See Scheduled backup tasks.

Reverse proxies 

X-Forwarded-Proto is freely settable by any client, so it is only honoured when REMOTE_ADDR matches trustedProxies (or the T3VAULT_TRUSTED_PROXIES environment variable, comma separated). Without this a client could claim an HTTPS connection and suppress the plaintext-transport warnings. The standalone restore installer reads the environment variable only, because it runs without a configuration file.

Backup directory 

Default (relative to the TYPO3 project root, parent of public/):

var/t3vault/backups
Copied!

This keeps archives outside the web root. Override via backupBaseDir in config.local.php or the Settings UI. Even AES-encrypted archives must not live under public/: ZIP metadata (file names, sizes, structure) is readable without the password.

The Settings API therefore refuses a target that

  • resolves into the web root (symlinks and .. are resolved first),
  • is a system location (/etc, /root, /usr, a bare home directory, …), or
  • lies outside the allowed roots.

Allowed roots are the project root plus everything declared in allowedBackupRoots (config.local.php) or in the T3VAULT_ALLOWED_BACKUP_ROOTS environment variable (comma separated). Use this for external volumes:

'allowedBackupRoots' => ['/srv/backups'],
Copied!

Declaring roots outside the API is deliberate — an authenticated session cannot widen its own target range. A directory that is already configured stays usable even if it is outside those roots, so existing installations keep working.

HTTP proxy (OpenTimestamps) 

Calendar submissions and proof upgrades need outbound HTTPS. Modes:

Mode Behaviour
auto Prefer TYPO3 HTTP options, then environment variables
typo3 Use TYPO3 HTTP / proxy settings only
env Use HTTP_PROXY / HTTPS_PROXY / NO_PROXY
custom Use httpProxy from config.local.php
none Disable proxy

A custom proxy is accepted as http(s)://[user:pass@]host[:port] or host:port; paths, whitespace and other schemes are rejected. Private and loopback addresses are allowed (corporate and DDEV proxies live there), but link-local ranges are not — 169.254.169.254 is a cloud metadata endpoint, never a proxy. Host names are additionally resolved once and rejected if they point into those ranges.

Self-invocation base URL 

Backup batches continue server-side via HTTP self-invocation. Behind a reverse proxy you may set Config::BACKUP_BASE_URL in the packaged API config to the public base URL of the API (example: https://example.com/t3vault/Api). Leave empty for auto-detection from the request.

Usage 

T3Vault is operated primarily through the embedded UI (backend module or standalone login). The sections below describe access, creating backups, and restoring them.

Standalone access 

The standalone app at /t3vault/ can be opened without TYPO3. This is intended for disaster recovery when the CMS is broken.

Enable marker 

Standalone password login is locked until a marker file exists (same idea as TYPO3's ENABLE_INSTALL_TOOL). Like TYPO3, the marker lives outside the web root:

var/t3vault/ENABLE_T3VAULT
Copied!

Rules:

  • File missing → login locked
  • File contains KEEP_FILE → permanently enabled
  • Otherwise → enabled for one hour after the file's modification time; the window is extended on every authenticated request (sliding window)
  • Ten failed login attempts consume the marker (logged in var/t3vault/state/auth.log)

After a successful standalone login the marker is removed (unless it contains KEEP_FILE).

T3Vault lock screen asking the operator to create var/t3vault/ENABLE_T3VAULT

Standalone login stays locked until the enable marker exists.

T3Vault standalone login form for the standalone password

Password login used for disaster recovery outside the TYPO3 backend.

Backend SSO 

When you open the module while logged in as a TYPO3 administrator, T3Vault issues a short-lived one-time claim ticket (?sso=… on the iframe URL). The embedded app redeems it via /auth/backend-claim and receives a backend session — without ENABLE_T3VAULT or the standalone password. The ticket is opaque, single-use and TTL-bound (not the former HMAC bt in the URL).

Backups 

Prerequisites: a backup encryption password is set in Settings.

Manual backups from New Backup open a dialog to include or exclude components (database, configuration, packages, Composer files, FAL/media, uploads). The job label reflects the selection (e.g. database-only is shown as a database backup, not a full site backup). Only a complete set (database + configuration + Composer / typo3conf) enables a full site restore; the restore installer warns and skips inapplicable steps for partial packages. Scheduled tasks of type full still back up all components.

New Backup dialog with checkboxes for database, configuration, packages, Composer files and media

Manual backups let you include or exclude components before the job starts.

Backup types 

full

Default. Everything needed to reinstall elsewhere (or a selected subset when started from the New Backup dialog).

Composer layout (config/ + public/):

  • database.zip (AES-256; entry database.sql.gz)
  • config/ (settings, additional, sites)
  • packages/ and other local Composer path repositories (e.g. extensions/ from composer.json)
  • composer.json / composer.lock
  • public/fileadmin/ and other local FAL storages (without _processed_/ / _temp_/)
  • optional uploads/, index.php, .htaccess

Classic layout (typo3conf/ in the document root):

  • database.zip (AES-256; entry database.sql.gz)
  • typo3conf/ (without temp/autoload caches)
  • fileadmin/, optional uploads/ (without _processed_/ / _temp_/)
  • index.php, .htaccess
  • Core (typo3/, typo3_src/) and typo3temp/ are not included
fileadmin
Media only (ZIP parts of fileadmin / FAL; without _processed_/ / _temp_/).
database
Encrypted database dump only (database.zip + MANIFEST.json). Useful for frequent scheduled tasks when a full site backup is not required every night.

Each completed backup also contains MANIFEST.json with layout-specific restore hints and environment metadata (PHP / DB / TYPO3 versions).

Scheduled backup tasks 

Backup tasks define what to back up, when, and how long to keep archives. Configure them in the UI (Backup tasks) or via the API (GET/POST /tasks). Definitions are stored in tasks.json inside the T3Vault data directory (0600).

Backup tasks dialog listing scheduled full-site and database jobs

Scheduled tasks define what to back up, when to run, and how long to keep archives.

Each task has:

  • typedatabase, fileadmin or full
  • schedulehourly (minute of hour) / daily / weekly / monthly (with at time) or a five-field cron expression
  • retention — one or more tiers { every: { value, unit }, keep } with unit in hour|day|week|month|year

Retention uses independent historical slots per tier, measured from the newest non-pinned backup of that task:

  • The finest tier (smallest every) covers ages [0, keep·E) in keep slots of length E (includes the newest backup).
  • Coarser tiers start one full period back: slots [E, 2·E)[keep·E, (keep+1)·E). So “1× month / keep 1” retains a backup about 30–60 days old, and “1× year / keep 1” about 365–730 days old (fixed durations: month = 30 days, year = 365 days).

Each slot keeps the newest backup in that age interval; the union of all tiers survives. Manual backups (no taskId) and pinned backups are never deleted by retention.

Run due tasks from cron (synchronous, no HTTP self-invocation loop):

php t3vault/cli/t3vault-run.php due
php t3vault/cli/t3vault-run.php task <id>
php t3vault/cli/t3vault-run.php retention
php t3vault/cli/t3vault-run.php list
Copied!

Or via the TYPO3 console (recommended on Composer installs):

vendor/bin/typo3 t3vault:run due
vendor/bin/typo3 t3vault:run task <id>
vendor/bin/typo3 t3vault:run retention
vendor/bin/typo3 t3vault:run list
Copied!

TYPO3 Scheduler (ext:scheduler suggested):

  • Dedicated task “T3Vault: run due backup tasks”, or
  • Task type Execute console commands → command t3vault:run (optional argument due, which is the default).

On the local DDEV project, scheduler:run is invoked every minute via Supercronic (.ddev/web-build/typo3.cron). After creating a Scheduler task in the backend, due T3Vault backups run automatically while ddev start is active — no host crontab needed.

Manual “Run now” in the UI starts the job via the normal web process chain. Backups created by a task carry taskId / taskName / mode in the plan and manifest so retention only touches that task’s archives.

What is never packed 

  • Symlinks leaving the backup scope. Because archiving follows symlinks, a link inside fileadmin/ that points to ~/.ssh/id_rsa, /etc/passwd or an .env outside the project would otherwise end up in the archive. Such links are skipped and listed as skippedSymlinks in the backup plan. Links that resolve back into the project are kept, so Composer path repositories in vendor/, public/_assets/ and the classic typo3/ links keep working.
  • System directories configured as FAL storage. A sys_file_storage row with a base path such as /etc, /root, /usr or a bare home directory is ignored. Regular storages outside the project root (for example /srv/shared-media) are still backed up as external parts.

How processing works 

  1. Start a backup from the UI (type full, fileadmin or database), or let a scheduled task / the CLI runner start it.
  2. A JSON plan is written under the backup directory.
  3. Web path: the server continues batches via self-invocation (CURL), so closing the browser does not stop the job — useful on shared hosting without cron. CLI path (t3vault/cli/t3vault-run.php): one process runs the plan to completion without HTTP continuation.
  4. When finished, integrity sealing runs (hashes + OpenTimestamps). Job status completed is independent of whether the stamp already reached Bitcoin blockchain confirmation (see Integrity & trust).

Optional batch options (API / advanced):

Option Default meaning
maxZipSizeMb Target size per ZIP part (default 500)
filesPerBatch Files per batch iteration (default 50)
maxTimePerBatch Seconds per batch (default 20)

Directory layout 

var/t3vault/backups/
└── backup_YYYYMMDD_HHMMSS_<id>/
    ├── backup-plan.json
    ├── backup-plan.json.state.json   # while running
    ├── backup.log
    ├── database.zip                 # AES-encrypted SQL dump (full backups)
    ├── MANIFEST.json
    ├── *_part_*.zip                 # AES-encrypted
    ├── integrity.json
    ├── integrity.json.hmac          # optional
    ├── integrity.json.ots
    └── integrity-meta.json
Copied!

backup-plan.json may also hold optional user fields name (label, max. 120 characters) and note (free text, max. 2000 characters). Edit them from the backup list (pencil icon). They are not part of the integrity seal, so changing them does not invalidate the archive signature. An empty value removes the field. Without a custom name, the UI shows the backup type label.

Error messages and references 

API responses keep infrastructure detail out: instead of an exception message with absolute paths, a failing request answers with a generic message and a reference such as ref: 9f2ac41b77e0. The full detail — exception class, message, file and line — is written to

var/t3vault/state/error.log
Copied!

Search that file for the reference to see what actually happened. The file is created with 0600 in the private directory, outside the web root.

Download 

From the backup list you can download:

  • .t3vault.tar — archive of the backup directory
  • Installer helpers for restore on a target server
  • DDEV project script (t3vault-ddev.sh) for local Composer-based reproduction

A browser download cannot send the Authorization header, so each download is authorised by a separate short-lived token in the URL (60 s; 120 s for the installer). The token is bound to one purpose, to one backup, and — on first use — to the requesting client. Repeat requests from the same client still work (redirects, resumed transfers), while the same URL from anywhere else is refused. The generated DDEV script therefore carries its own archive token (one hour), because it runs later and from another client than the browser.

Importing backups 

An upload (.t3vault.tar or loose artifacts) is content from outside, so it is checked before it appears in the backup list.

Request limits. At most uploadMaxFiles files, uploadMaxFileBytes per file and uploadMaxTotalBytes in total (see Settings overview). The import is also refused when the target volume has less than roughly twice the payload free, because a TAR import stages the archive and then writes the extracted artifacts. Only one import runs at a time.

Structural validation. Every ZIP part is inspected with the same rules the restore installer applies: absolute entry paths, .. segments, backslashes and symlink entries are rejected, as is an implausible compression ratio. A rejected upload is deleted immediately and answered with a log reference — no half-imported directory stays behind.

Trust marking. A backup created here is sealed: integrity.json lists the artifact hashes and integrity.json.hmac binds that list to this installation's signing secret. An import is therefore rated:

Rating Meaning
verified Hashes match integrity.json and the HMAC verifies with the local secret — sealed here, unchanged
unverified Anything else: no integrity.json, altered artifacts, a seal from another installation, or no local signing secret

A foreign archive cannot verify here — that is expected, not an attack, and it is reported as such. The list and the details dialog show the rating with its reason, and the generated RESTORE.md of an unverified import carries a warning: restoring an archive of unknown origin introduces its code and data into the target system.

integrity-meta.json from an upload is discarded. It records the result of a verification run on the source system and must not be shown as this installation's verification state.

Restore 

Restore confirmation dialog warning that the current installation will be overwritten

Restoring from the UI prepares the installer in the document root and switches the site into restore mode.

Target server (installer) 

  1. Copy the downloaded .t3vault.tar (and installer files if provided) to the target machine.
  2. Run the installer / follow the UI import flow in a fresh T3Vault standalone app on the target.
  3. Enter the same backup encryption password used when the backup was created.
  4. Choose the target layout if it differs from the backup source (MANIFEST.json records Composer vs. classic):

    • Restore a Composer backup as classic/legacy (document root) when the target host has no Composer-based deploy.
    • Restore a classic backup as Composer when you want config/ + public/ (or a DDEV project).
  5. Complete Composer install / TYPO3 setup as required by the chosen target layout.

Transport security 

The installer refuses to run over plain HTTP, because the restore transfers the backup password and the database credentials. Accepted as secure: real HTTPS, HTTPS terminated by a proxy listed in T3VAULT_TRUSTED_PROXIES, or a client on the loopback interface — for example through an SSH tunnel:

ssh -L 8443:localhost:80 user@target
# then open http://127.0.0.1:8443/installer.php
Copied!

Only if the network is demonstrably trusted, the requirement can be waived by creating the file .t3vault-allow-insecure next to the installer (or by setting T3VAULT_ALLOW_INSECURE=1). The environment check then shows a warning for the connection instead of a green check.

The step Write configuration additionally accepts an optional encryptionKey. Leave it empty to have a fresh key generated; supply the key of the original installation if hashes stored in the database (e.g. install tool secrets) have to stay valid.

Site base URL 

If you enter a Site base URL, the installer updates a single primary site under config/sites/*/config.yaml (or classic typo3conf/sites/) as follows:

  • the primary site is the one with rootPageId: 1, otherwise the lowest rootPageId;
  • only that site’s top-level base is set to the new URL;
  • existing baseVariants (production, staging, old DDEV hosts, …) stay as they are;
  • for *.ddev.site hosts, at most one Development/DDEV variant is added or refreshed;
  • absolute language bases such as https://en.example.com/ become a path on the new host (https://recover.ddev.site/en/).

Leave the field empty to skip site-config changes entirely.

If the target host is already defined in any site’s top-level base or baseVariants, the installer leaves all site URLs unchanged (restore onto a known domain).

Multi-site: other sites keep their original bases and are not reachable on the recover host until you add hostnames (for example DDEV additional_hostnames) or map them manually.

DDEV (local) 

Use Download → DDEV project or the scripts inside the TAR (t3vault-ddev.sh, t3vault-ddev.php, DDEV.md).

Typical flow:

  1. Run the script; it loads the archive (or uses a local .t3vault.tar).
  2. Enter the backup password.
  3. The script unpacks AES ZIPs, converts the layout toward Composer if the source was classic, writes .ddev/config.yaml from environment metadata, starts DDEV, imports the database, and runs composer install. (The web installer also supports the opposite direction: Composer → classic/legacy.)

Verify before restore 

Before trusting an archive from untrusted storage, verify that it was not modified after sealing (see How to verify a backup (recommended)):

php t3vault-verify.phar /path/to/backup_dir
Copied!

Optionally confirm the OpenTimestamps seal with Manual verification with the OpenTimestamps client.

Integrity & trust 

Overview 

When a backup finishes, T3Vault seals it with BackupIntegrityService:

  1. Hash artefacts (*.zip incl. database.zip, legacy database.sql.gz / database.sql, MANIFEST.json) with SHA-256.
  2. Write integrity.json (algorithm, createdAt, file digests).
  3. Optionally write integrity.json.hmac (HMAC-SHA256 bound to the site secret).
  4. Submit the digest of integrity.json to OpenTimestamps calendars and store the proof as integrity.json.ots.
  5. Write integrity-meta.json (stamp outcome metadata — not a trust anchor).

Trust model:

  • Changing backup files without a new seal breaks the hash check.
  • Forging a new seal requires a new OpenTimestamps proof.
  • Final time-stamp time comes from the Bitcoin blockchain, not this server.
  • A large gap between createdAt and that blockchain time is treated as suspicious (T3Vault allows up to 72 hours for calendar → chain confirmation).
  • Until the proof is fully attested, status stays pending.
  • HMAC is only checked when the site secret is available; on foreign hosts it is skipped so cross-system restore still works.

Verification states 

State Meaning
ok Hashes match and OTS proof is attested within the time window
pending Hashes OK; calendar proof not yet upgraded to Bitcoin
missing Integrity files incomplete
stamp_failed Stamping failed earlier; hashes may still be OK
tampered Hash or OTS digest mismatch / attestation skew
checking Transient UI state while a check runs

Verify with T3Vault CLI 

T3Vault ships an integrity verifier that re-hashes artefacts and checks the OpenTimestamps receipt (via ElephStamp).

This is the recommended check before restore: it needs no Bitcoin node and answers whether the backup files still match integrity.json. For an optional independent check of the .ots file with the official client, see Manual verification with the OpenTimestamps client.

Usage 

From a development checkout:

php t3vault/cli/t3vault-verify.php [--json] [--quiet] [--lang=de|en] <backup-dir>
Copied!

Or with the offline PHAR (built into the package under Installer resources):

php t3vault-verify.phar [--json] [--quiet] [--lang=de|en] <backup-dir>
Copied!

Example:

php t3vault-verify.phar \
  /var/www/html/var/t3vault/backups/backup_20251117_143022_a3f8b2c1
Copied!

Options:

Option Description
--json Print the full result object as JSON
--quiet Suppress human-readable output (exit code only)
--lang= de or en for messages

Exit codes:

Code Meaning
0 ok
2 pending / stamp_failed / missing
1 tampered or fatal error

Manual verification with the OpenTimestamps client 

You can check integrity.json.ots independently of T3Vault using the official OpenTimestamps command-line client. Combined with a hash check of the backup artefacts, this answers: has anything in this backup been changed since it was sealed?

Upstream project: opentimestamps-client.

For the recommended overall flow, see How to verify a backup (recommended).

Install the client 

Python package (typical on Debian/Ubuntu):

sudo apt install pipx
pipx install opentimestamps-client
# or: pip install opentimestamps-client
Copied!

Confirm the binary:

ots --help
Copied!

What was stamped? 

T3Vault stamps only integrity.json (not the ZIP files themselves). The ZIP digests are listed inside integrity.json. Therefore:

  1. Confirm artefact hashes still match integrity.json (re-hash yourself or use Verify with T3Vault CLI).
  2. Confirm integrity.json.ots is a valid OpenTimestamps proof for that exact integrity.json file.

Always keep integrity.json and integrity.json.ots side by side in the same directory (the ots client assumes the target file name by stripping .ots).

Check that files were not modified 

Independently of ots, confirm that backup artefacts match the seal:

cd /path/to/backup_YYYYMMDD_HHMMSS_<id>
sha256sum integrity.json
# Compare with digests listed under "files" in integrity.json, e.g.:
sha256sum database.zip MANIFEST.json *_part_*.zip
Copied!

If any artefact hash differs from integrity.json, the archive was modified after sealing — do not restore it, regardless of OTS status.

Prefer the bundled verifier for the same check:

php t3vault-verify.phar /path/to/backup_YYYYMMDD_HHMMSS_<id>
Copied!

Verify the OpenTimestamps proof 

ots verify checks that integrity.json still matches the proof and (when Bitcoin Core RPC is available) that the attestation is anchored in a Bitcoin block:

cd /path/to/backup_YYYYMMDD_HHMMSS_<id>
ots verify integrity.json.ots
Copied!

Expected success:

Assuming target filename is 'integrity.json'
Success! Bitcoin block <height> attests existence as of <date>
Copied!

Pass the target explicitly if needed:

ots verify -f integrity.json integrity.json.ots
Copied!

Inspect the proof without a Bitcoin node:

ots info integrity.json.ots
Copied!

Without installing ots, you can also use the browser verifier — see Verify in the browser (opentimestamps.org).

Verify in the browser (opentimestamps.org) 

The official site opentimestamps.org can verify an .ots proof without Bitcoin Core or the ots CLI. Hashing runs in your browser (the stamped file content is not uploaded for hashing beyond what the page needs locally).

Steps:

  1. Open the backup directory and keep both files ready: integrity.json and integrity.json.ots.
  2. Open https://opentimestamps.org/ and use Stamp & Verify.
  3. Drop (or select) integrity.json.ots as the proof (.ots).
  4. Drop (or select) integrity.json as the stamped file when the page asks for it.

A successful chain confirmation shows the OpenTimestamps UI label Bitcoin Attestation (block / time). Pending calendars may still show Pending/Other Attestation until the proof is confirmed on the Bitcoin blockchain.

What to watch out for 

  • Only integrity.json was stamped — never drop a ZIP, MANIFEST.json, or the whole .t3vault.tar as the stamped file. Those will not match the proof.
  • Use the exact integrity.json from the same backup as the .ots file. Any edit (whitespace, re-save) changes the digest and verification fails.
  • The website checks the seal (integrity.json.ots). It does not re-hash *_part_*.zip / database.zip. Always run Verify with T3Vault CLI (or the manual hash check above) as well if you need to know the archive contents were not modified.
  • Right after backup creation the proof may still be pending. That is not the same as “tampered”; wait for calendar → Bitcoin confirmation and try again, or rely on the hash check in the meantime.
  • Do not use the site’s stamp action on backup files — T3Vault already created the proof. You only need verify.

Bitcoin node for ots verify 

Full CLI ots verify needs Bitcoin Core RPC (local node or remote URL). Without it you typically see:

Could not connect to Bitcoin node: Cookie file unusable
([Errno 2] No such file or directory: '/home/…/.bitcoin/.cookie')
and rpcpassword not specified in the configuration file: '…/bitcoin.conf'
Copied!

That error means the Bitcoin-blockchain time stamp was not checked — it does not mean the backup files were modified. For the “unchanged files” question, rely on the hash check / Verify with T3Vault CLI. For the seal without a local node, use Verify in the browser (opentimestamps.org).

Local Bitcoin Core (default cookie auth under ~/.bitcoin/):

# bitcoind must be running and synced far enough for the attested height
ots verify integrity.json.ots
Copied!

Explicit RPC URL (local or remote):

ots --bitcoin-node http://USER:PASS@127.0.0.1:8332/ verify integrity.json.ots
Copied!

Troubleshooting 

Symptom Meaning / next step
Hash mismatch vs. integrity.json Archive altered — do not restore
Web verifier: stamped file does not match Wrong file dropped — use integrity.json from the same backup
Web verifier: pending / no chain time stamp yet Proof not on-chain yet; hashes may still be OK
Could not connect to Bitcoin node / missing Use
.cookie Verify in the browser (opentimestamps.org) or configure RPC
ots not found under sudo pipx installs for your user only — run without sudo

Sitemap