Home / Blog / License delivery
License delivery

Why does a time-limited license break when a customer rolls back the system clock?

Trials, grace periods, and expiry dates all rest on a value the user can edit in ten seconds. Here is how clock changes actually break licensing, and how to defend against them without locking out people whose only crime is a dead battery.

Close-up of a person's hands turning the winding knob on the back of a small round metal desk clock, sunlit wooden table, blurred office window and green plants behind, shallow depth of field

The system clock is the softest input your app has

Every time-limited license depends on the answer to one question: what time is it? On a desktop machine, the default answer comes from a value the user can change in a settings panel in about ten seconds. It is not signed, not audited, and not protected in any meaningful way. Trial expiry, subscription grace periods, and update entitlement windows all sit on top of it. That is a workable starting point, and it is also the reason the oldest trick in shareware still works: set the date back, get another thirty days. Related: What is the best way to structure trial keys so they convert to paid?

It helps to stop treating clock changes as attacks and start treating them as noise. Most of the movement you will see is entirely legitimate. A dead CMOS battery drops a desktop back to a manufacturing default. A virtual machine restored from a snapshot wakes up weeks in the past. A laptop that sat in a drawer syncs forward the instant it reconnects. Container images start at whatever the host decided. If you design for the noise first, the deliberate rollback mostly handles itself as a side effect.

Keep reading: Why Digital Products Need Licensing, Generating License Keys That Work, Validating Keys Without Frustrating Users. See how KeyDispense helps you digital product license key issuing and validation api.

Three failure modes, and which one actually costs you money

Rollback is the mode teams worry about, and it is the least damaging of the three. Someone sets the date back and stretches a trial. You lose a trial, not a customer, and the person willing to do that was usually never going to pay. The expensive failure runs the other direction. A clock that jumps forward, whether from a bad sync, a mistyped year, or a timezone bug in your own date math, tells your application that a valid paid license has expired. Now a paying customer is locked out in the middle of real work, and you are the one who broke it. Related: Generating License Keys That Work

The third mode is quieter and far more common than either. Your app compares timestamps produced by different clocks. The license was issued using your server time, the expiry is evaluated using machine time, and the two are minutes or days apart. Add a conversion that reads a stored date string as local when it was written as UTC, and licenses start expiring several hours early for a large share of the world. When a clock ticket arrives, this is the first thing to rule out, because most reported tampering turns out to be arithmetic.

Defenses that do not punish honest users

The cheapest useful defense is a monotonic high water mark. Each run, record the highest timestamp the app has ever observed, stored next to the activation record. If the current clock reads earlier than that mark by more than a small tolerance, you know time moved backward. Treat that as a signal rather than a verdict: keep counting the trial forward from the high water mark instead of from the reported time, and say nothing at all. The user with a dead battery never notices anything, and the rollback simply stops producing extra days.

When the machine is online, let the validation response carry your server timestamp and treat that as the authority for anything that expires. The app keeps it with the signed license payload, uses it for expiry math, and falls back to local time bounded by the high water mark while offline. Give offline sessions a generous tolerance, typically measured in days rather than minutes, and let the license keep working until the next successful check. Fail open on ambiguity. An unreachable server or a confused clock should never end a work session that is already underway. Related: How do you activate a license when the customer's machine never touches the internet?

Choosing a posture, writing the message, and testing it properly

Match strictness to stakes. A free trial deserves the high water mark and nothing more, because a false positive there earns you an angry public review from someone who has not paid you yet. A paid perpetual license should almost never expire on clock evidence alone. A subscription can be firmer, but the firm part belongs on your server at renewal time, where you have real payment data, not in a date comparison buried inside the binary. And never show a message accusing the customer of tampering. The person reading it is far more likely to have a broken clock than bad intentions, and that wording is unrecoverable. Related: What should a developer do when a license key leaks on a public forum?

Test this deliberately, because it will not surface in ordinary QA. Build a small matrix and run both the trial path and the paid path through every row: clock set back one day, back one year, forward one year, reset to the hardware default, offline for the entire grace period, and restored from an old virtual machine snapshot. Write down exactly what the user sees in each case. Then log clock anomalies with both the local and the server timestamp attached, so when a ticket lands you can tell a failing battery from a rollback in one glance instead of guessing.

Key takeaways
  • Local system time is user-editable input, so treat it as a hint rather than a fact.
  • A clock jumping forward locks out paying customers, which costs more than the trials a rollback quietly steals.
  • Store a monotonic high water mark and count trials from it, without ever telling the user.
  • Use server time from validation responses as the expiry authority, with a generous offline tolerance and a fail open default.
Julien Jimenez
Written by

Julien Jimenez

Julien Jimenez is an independent software builder based in Paris. He designs, ships, and operates focused SaaS products for small businesses and independent professionals. Read the full author page.

Issue and validate license keys with one API

Digital product license key issuing and validation API. KeyDispense is built to help you put this into practice.

Get an API key

More from the KeyDispense blog

Get the KeyDispense playbook

Practical guides on license delivery, straight to your inbox as we publish them. No spam, unsubscribe any time.

By subscribing you agree to our privacy policy.