TSA
TSA is a self-hosted RFC 3161 Timestamp Authority for NethServer 8, based on the sigstore timestamp-server. It issues and verifies trusted timestamp tokens for documents and digital signatures.
Announcement and talks
The TSA app announcement
TSA is a community application from the tebbiworld repository. Documentation and source can be found on Github
What is a Timestamp Authority and when do I need one?
A Timestamp Authority (TSA) confirms that a piece of data existed at a given point in time. The client sends only a hash of the document to the TSA; the TSA returns a signed token (RFC 3161) that binds this hash to the current time. The document itself is never transmitted.
Typical use cases:
- Signed PDFs (LibreSign, JSignPdf, other PAdES tools): without a TSA, the signature contains only the local clock of the signing server (
signing_time). Adobe Acrobat and other validators treat this as untrusted. With an embedded RFC 3161 token, the signing time is confirmed by an independent signer. - Evidence that a file existed at a specific time (lab records, source code, exports, log archives) using
openssl ts. - Many files at once: create a list of hashes (e.g.
sha256sum) and timestamp that list. One token then covers all listed files.
Public TSA or self-hosted?
Public services such as freeTSA.org offer free RFC 3161 timestamps. This module is an alternative for cases where you want to run the service yourself.
| Aspect | Public TSA (e.g. freeTSA.org) | This module (self-hosted) |
|---|---|---|
| Operator | Third party | You, on your own NS8 node |
| What leaves your network | Document hash only | Nothing (unless clients are external) |
| Availability | Depends on the public service | Depends on your node |
| Signing certificate | Operated by the provider | Your own chain, or an uploaded commercial timestamping certificate |
| Trust by third parties | Depends on the provider | Clients must import your Root CA |
| Suitable for | Occasional use, no own infrastructure | Automated signing workflows on your own cluster (e.g. LibreSign on Nextcloud) |
Evidentiary value: a self-operated TSA is suitable for internal and technical use. Against third parties, its tokens carry limited legal weight compared to an accredited, audited provider. If qualified (e.g. eIDAS) timestamps are required, obtain a commercial timestamping certificate and install it via Upload certificate (see below).
Architecture
- A single distroless, rootless container (sigstore
timestamp-server, UID 65532). - The container listens on
127.0.0.1only and is published through the Traefik instance of the node on which the module runs. - HTTPS, Let's Encrypt and HTTP→HTTPS redirect are handled by Traefik, exactly like in other NS8 web applications.
Two separate certificates
This is the most important concept of the module. The two certificates have different purposes and cannot replace each other.
| HTTPS transport certificate | RFC 3161 signing certificate | |
|---|---|---|
| Purpose | Encrypts the connection between client and server | Signs every timestamp token |
| Issued by | Let's Encrypt (via Traefik), optional | Module itself (self-signed chain) or manual upload |
| Renewal | Automatic by Traefik | Explicit action in the module UI (Rotate) |
| Lifetime | Short (Let's Encrypt default) | Root CA approx. 30 years, leaf approx. 25 years |
| Algorithm | Traefik default | EC P-384, leaf key stored as AES-256 encrypted PKCS#8 |
Why Let's Encrypt cannot sign timestamps: RFC 3161 requires the extension extendedKeyUsage = timeStamping marked as critical in the signing certificate. Public TLS certificates, including Let's Encrypt, do not contain this extension. The signing certificate is therefore always either the self-generated chain or an externally obtained timestamping certificate.
The signing chain is generated on the first save of the configuration. Existing certificates are never overwritten when the configuration is saved again.
Installation
On a NethServer 8 node:
add-module ghcr.io/tebbiworld/tsa:latest 1
Then open the module UI and configure:
- Host name (FQDN), e.g.
tsa.example.org - Let's Encrypt (optional): requires that ports 80/443 of this FQDN reach the node on which the module runs (HTTP-01 challenge).
- HTTP→HTTPS redirect (optional)
On the first save, the module generates the signing chain and starts the server.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| POST | /api/v1/timestamp | Submit a timestamp query (Content-Type application/timestamp-query) |
| GET | /api/v1/timestamp/certchain | Download the signing certificate chain |
| GET | /ping | Health check |
Timestamp URL for clients: https:<fqdn>/api/v1/timestamp.
=====Certificate page=====
The Certificate page of the module UI provides:
* Current signer: subject, valid-until date, SHA-256 fingerprint
* Download chain
* Self test: issues a real token and verifies it against the chain
* Rotate: archives the current chain and generates a new one
* Upload certificate: installs an externally obtained timestamping certificate. The certificate is checked for the timeStamping EKU before anything is replaced.
Note on rotation: tokens issued before a rotation can only be verified against the old chain. Keep the archived chain as long as old tokens need to be verifiable.
=====Usage=====
====Command line (openssl)====
Replace tsa.example.org with your FQDN.
1. Create a timestamp query for a file:
<code bash>
openssl ts -query -data file.pdf -sha256 -cert -out file.tsq
</code>
2. Send the query to the TSA:
<code bash>
curl -H “Content-Type: application/timestamp-query” –data-binary @file.tsq https://tsa.example.org/api/v1/timestamp -o file.tsr
</code>
3. Download the chain (once):
<code bash>
curl -o tsa-chain.pem https://tsa.example.org/api/v1/timestamp/certchain
</code>
4. Show and verify the token:
<code bash>
openssl ts -reply -in file.tsr -text
openssl ts -verify -in file.tsr -queryfile file.tsq -CAfile tsa-chain.pem
</code>
Expected result: Verification: OK
For strict verification, use only the Root CA certificate as -CAfile (the trust anchor), not the complete chain.
====Timestamping many files====
<code bash>
find /path/to/files -type f -exec sha256sum {} + > hashes.txt
openssl ts -query -data hashes.txt -sha256 -cert -out hashes.tsq
</code>
Continue with step 2. Keep hashes.txt, the files and the token together: verification requires all of them.
====LibreSign / JSignPdf====
Set the TSA URL to https:<fqdn>/api/v1/timestamp
LibreSign note: in the tested setup (LibreSign 13.2.6), the PhpNative signing engine did not embed the TSA token although a TSA URL was configured. With the JSignPdf engine, the token was embedded.
Adobe Acrobat and other validators
Validators must trust the Root CA of your TSA. Download the chain from /api/v1/timestamp/certchain and import the Root CA certificate as a trusted certificate in the validating application (in Acrobat: Trusted Certificates). Without this step, the timestamp is displayed as not verifiable, even though the token is technically valid.
Backup and security
- Back up the Root CA private key offline. It is the trust anchor of the whole TSA. Whoever holds it can issue tokens that your clients accept.
- Keep archived chains after each rotation (see above).
- The accuracy of the timestamps depends on the system clock of the node. Make sure time synchronisation (NTP/chrony) works on the node.
Health check
curl -i https://tsa.example.org/ping
For a full functional test, use the Self test on the Certificate page, or the openssl sequence under Usage.
Disclaimer
Private hobby project, shared as-is. No warranty, no guaranteed support, use at your own risk. Unofficial community package, not affiliated with or endorsed by the sigstore project. The module pulls the upstream sigstore timestamp-server image; the module's own code is licensed GPL-3.0-or-later. Bug reports via GitHub issues.