Hello Anvil team,
I’m working on Anvil for few years now and really appreciate this tool !
I would like to report what looks like to me a potentially dangerous App Server schema migration issue related to the new “client_hidden” metadata on Data Table columns.
Context
I’m running an Anvil app on a Pi5 server at work, for over a year, using:
-
Anvil App Server
-
PostgreSQL data base
-
Docker Swarm
-
A self-hosted production app
-
A standard Anvil Users table used for login/password authentication
The app is deployed from a Git repository. The app folder is mounted into an App Server container.
Before the issue, my App Server config had:
auto-migrate: true
The problem came up after the new implementation of the “client visible” data columns:
After a normal app update from Git, users could no longer log in with their existing passwords. They had to use “forgot password” to regain access.
What I noticed:
The App Server automatically applied a migration that included this:
Data tables schema out of date. Applying migrations:
({:type :DELETE_COLUMN, :table "users", :column_name "password_hash"}
{:type :DELETE_COLUMN, :table "users", :column_name "centre"}
...
[INFO anvil.app-server.tables] Migrating automatically...
[INFO anvil.app-server.tables] Migration complete.
This caused existing user passwords to stop working, because the users.password_hash column was deleted and recreated.
After using “forgot password”, login worked again, because a new password hash was written into the new password_hash column.
The PostgreSQL table physically contained the expected columns:
password_hash text
accept_data boolean
centre bigint
So the PG SQL columns were not missing.
But the relevant part of anvil.yaml contained these columns with explicit client_hidden: false metadata:
users:
client: search
columns:
- admin_ui: {order: 3, width: 138}
client_hidden: false
name: password_hash
type: string
- admin_ui: {order: 22, width: 200}
client_hidden: false
name: accept_data
type: bool
- admin_ui: {order: 22.5, width: 200}
client_hidden: false
name: centre
target: lieux
type: link_single
With this YAML, App Server reported the following proposed migration:
Data tables schema out of date. Here is the migration that will run if you restart Anvil with the --auto-migrate command-line flag:
({:type :DELETE_COLUMN, :table "users", :column_name "accept_data"}
{:type :DELETE_COLUMN, :table "users", :column_name "password_hash"}
{:type :DELETE_COLUMN, :table "users", :column_name "centre"}
{:type :ADD_COLUMN,
:table "users",
:column
{:admin_ui {:order 3, :width 138},
:client_hidden false,
:name "password_hash",
:type "string"}}
{:type :ADD_COLUMN,
:table "users",
:column
{:admin_ui {:order 22, :width 200},
:client_hidden false,
:name "accept_data",
:type "bool"}}
{:type :ADD_COLUMN,
:table "users",
:column
{:admin_ui {:order 22.5, :width 200},
:client_hidden false,
:name "centre",
:target "lieux",
:type "link_single"}})
This is the dangerous part: App Server appears to treat the explicit client_hidden: false metadata as a schema difference requiring DELETE_COLUMN and ADD_COLUMN, instead of treating it as a metadata-only change.
What I tried: (and it worked)
When I removed the explicit client_hidden: false lines from anvil.yaml, leaving the columns like this:
- admin_ui: {order: 3, width: 138}
name: password_hash
type: string
- admin_ui: {order: 22, width: 200}
name: accept_data
type: bool
- admin_ui: {order: 22.5, width: 200}
name: centre
target: lieux
type: link_single
the App Server started normally and reported:
Running Anvil Database Migrator for (base data-tables runtime) DB.
Database is already up to date at version '2025-06-09-recreate-table-views'
[INFO anvil.core.server] HTTP Server running on port 80
So removing the explicit client_hidden: false metadata made the schema match again.
Does it seem like a bug ?
A change such as:
client_hidden: false
on an existing column should not result in:
DELETE_COLUMN users.password_hash
ADD_COLUMN users.password_hash
Especially on the Users table, this is dangerous because deleting and recreating password_hash invalidates all existing user passwords.
In my case, because `auto-migrate: (“true” was enabled), the destructive migration was applied automatically in production.
Current workaround
I have now changed my production config to:
auto-migrate: false
I temporarily used:
ignore-invalid-schema: true
to restart the app without applying the destructive migration.
Then I restored PostgreSQL from a backup and removed the explicit client_hidden: false lines from anvil.yaml. After that, the App Server considered the database schema up to date again.
Should really App Server generate DELETE_COLUMN / ADD_COLUMN for an existing column only because “client_hidden: false” appears explicitly in anvil.yaml file ?
At minimum, should App Server not suggest or automatically apply destructive migrations on sensitive Users table columns such as:
users.password_hash
when the SQL column already exists and only metadata differs.
Actual behavior
App Server proposed and, with auto-migrate: true, applied:
DELETE_COLUMN users.password_hash
ADD_COLUMN users.password_hash
This invalidated existing user passwords.
Impact
This is a serious production risk for self-hosted App Server users, especially those using:
auto-migrate: true
because it can silently destroy users.password_hash and force all users to reset their passwords.
Could you please confirm whether this is expected behavior or a migration bug related to the new client_hidden column metadata?
Again, thank you very much for your work !