I started VALYVRA with a problem that has little to do with software: owning a valuable asset does not mean being able to sell it when you need to.
An owner may face a long wait for a buyer willing to pay a fair market price. Selling sooner can mean accepting a substantial discount. I wanted to explore an alternative way to connect the value of an asset with demand from participants.
That is the motivation behind VALYVRA. One approach I am exploring is a platform for prize draws involving real-world goods and experiences, with blockchain-based verification of parts of the process.
The broader liquidity goal is still a hypothesis. A working prototype cannot establish demand, validate an asset’s price, or guarantee a successful sale. Our current blockchain experiments use testnet assets.
As the founder, I am documenting the development process, including the less visible work. Two recent improvements in our authentication service may be useful to anyone building a Python application with SQLite.
The relevant part of our stack is a Flask authentication service running under Gunicorn with two worker processes. Nginx forwards requests to it, and SQLite stores account and authentication state. Separate application services and background workers handle the testnet features.
This means two requests can reach the same authentication flow concurrently. A successful manual login tells us very little about how that flow behaves under contention.
Consider a pending two-factor authentication challenge. It has an expiration time, an attempt counter, and a user associated with it. We want a strict limit on admitted attempts and at most one session created from that challenge.
A sequence like this needs a concurrency policy:
Read challenge
Check attempts and expiration
Verify code
Increment attempts or create session
If competing requests make decisions from the same earlier state, the checks alone do not enforce the intended behavior.
We changed the verification flow to use two short transactions:
- Reserve an attempt atomically: check the challenge and user state, increment the counter, then commit.
- Perform the code or external-provider verification outside that write transaction.
- Start a new transaction and re-read the challenge and user. Verify that the challenge is still valid and the relevant state has not changed.
- Consume the challenge and create the session within that same transaction. When a recovery code is used, mark it as used there too. We use BEGIN IMMEDIATE for these write transactions. SQLite permits one writer at a time, and beginning an immediate transaction can fail with SQLITE_BUSY if another writer already holds it. Short transactions and explicit handling of failures still matter. SQLite transaction documentation. The separation around the provider call is deliberate. Waiting for a network response while holding the database’s writer lock would delay unrelated writes. Re-reading afterward is equally important: a challenge could expire or a user could be blocked while the verification is in progress. The second lesson was about connection lifetime. In Python’s sqlite3 module, a connection’s context manager handles transaction commit and rollback, but it does not close the connection. Python’s SQLite documentation. For our existing transaction configuration, we made both responsibilities explicit: from contextlib import closing
# db_connect() returns a configured sqlite3.Connection.
with closing(db_connect()) as db:
with db:
db.execute(
"INSERT INTO audit_events (event_type) VALUES (?)",
("example_event",),
)
This is an illustrative table and operation. The inner context completes the transaction; the outer context closes the connection afterward. The same cleanup happens on exceptions and early returns. In the application, we also close the connection if its initial configuration fails.
We updated 27 transaction contexts with explicit connection closure. I am not attaching a performance claim to that change: we verified the lifecycle behavior, not a throughput improvement.
The combined authentication test suite now has 44 tests covering two-factor verification, rate limits, connection cleanup, and Flask integration. Some exercise extracted functions with test doubles; nine run through the Flask application. They are not 44 end-to-end tests of the entire platform.
Useful cases included competing successful submissions creating only one session, concurrent invalid submissions respecting the attempt budget, and session-creation failure rolling back challenge consumption. We also checked that an external verification call did not hold the writer lock, and that connections closed after commit, rollback, and a failed commit.
Before replacing the running authentication container, we ran the tests in a separate candidate image with networking disabled and temporary databases. After deployment, we checked service health and manually tested login with two-factor authentication.
These changes address specific behaviors in the authentication service. They do not constitute an independent security audit of VALYVRA or its smart contracts.
The project is still an experiment in connecting a real-world problem with a workable product. Blockchain verification is one part of that work. Account access, state transitions, asset valuation, and eventual delivery each need their own evidence and checks.
For developers who have built similar systems: how do you structure tests for authentication flows that combine local state with an external verification provider? I would particularly welcome examples involving expiration, retries, and concurrent requests.
Disclosure: This draft was prepared with AI assistance using my project notes, source code, and test results.
Top comments (0)