Your project's own files
An app's source, scripts, docs and fixtures sync with the rest — kept for the team, never served; .webmatiignore, the secret check, history, diff and restore without git.
A project holds more than the website: a React app's source in app/, a deploy
script, notes in docs/, test data in backend/fixtures/, a README. Every file
that no part of the website owns is a project file. The platform keeps them
for your team, so the next person who runs webmati clone gets them too.
- They are never served: no visitor, page or workflow reads them, so pushing
one changes nothing on the live site. A plain
webmati pushsends them — no--live, nothing to publish. - They sync exactly like everything else: the same letters in
webmati status,pullbrings your colleagues' changes,pushrefuses to overwrite one you have not pulled,resolvesettles a conflict.
webmati status # A app/src/App.tsx — added
webmati push -m "login form" # project files go with the rest of the push
webmati history # one snapshot per push: who, when, the note
You do not need git for any of it: the few things a team opens git for are commands here (see History without git).
What is a project file, and what is not
Everything under the project folder except:
- the website's own parts:
theme/,backend/workflows/,backend/tables/,backend/routes.json,variables.json,secrets.json,workers.json,pages/,content/,redirects.json,translations/,wellknown/; - the CLI's own files:
webmati.toml,.webmati/, theAGENTS.mdandCLAUDE.mdfilesclonewrites, and.git/; - whatever
.webmatiignoreor any.gitignoreleaves out.
.webmatiignore sits at the project root and uses gitignore syntax. clone
writes this default (a project without one gets the same rules):
node_modules/
dist/
build/
.next/
.vite/
coverage/
*.log
.DS_Store
.env*
!.env.example
*.pem
*.key
id_rsa*
.webmati/
So an app's dist/ and node_modules/ never go: visitors get the build only
through webmati app deploy. Every .gitignore in the project is honoured as
well, the nested ones included (the one in app/ too). Edit .webmatiignore to
leave more out; it is a project file itself, so the whole team shares the rules.
An ignore rule never removes a file
A file that is already synced stays synced when a rule starts matching it — as in git. To stop syncing it, delete it and push. And a file on the server that your own rules leave out is neither downloaded nor deleted:
statusandpullsay how many there are.
Credentials never leave your machine
Before anything is uploaded, push reads every text file for credential-shaped
values — a private key, a platform, Stripe, GitHub, AWS or npm key, a password in
a URL — and refuses the push, naming the file and the line:
Push refused by the offline checks (the server would refuse these too):
app/src/pay.ts:2: error secret: this line holds what looks like a Stripe secret key — it would leave this machine
fix: read it from an environment variable (or a backend secret: `webmati tool call set_backend_secret`), then push again
--no-validate does not skip it, and the server checks again. Files named like
credentials are refused whatever they hold: .env (but not .env.example),
*.pem, *.key, id_rsa*, .netrc, credentials.json, Terraform state.
webmati validate shows the same findings before you push.
Pull, conflicts and merges
pull downloads new and changed files, deletes the ones a colleague deleted,
and keeps a script's executable bit (chmod +x is a change too). When a file
changed on both sides it merges:
- text by line, like git — overlapping edits get
<<<<<<<markers; .jsonkey by key (atsconfig.jsonwith comments merges by line);- a binary file (an image, a font) cannot merge: it is a conflict, with the
server's copy in
.webmati/conflicts/<path>.remote.
When your push is refused because a colleague pushed first, just webmati pull:
it merges their change into yours, then push again. Pull says whose change it
took, merged or conflicted with, and in which snapshot:
S docs/setup.md — changed on the server by lan@shop.vn · webmati-cli@lan-mac in #14
See Conflicts.
History without git
Every push that changes project files records a snapshot of the whole set:
its number, who (the person's account, and the machine the key is on), when,
your -m note and what changed. The newest 50 are kept, none older than 90 days
but the newest.
webmati history # every snapshot, newest first
webmati history docs/setup.md # only the ones that changed this file (or a folder)
webmati diff docs/setup.md --at 12 # what changed since snapshot 12 (--since 12 is the same)
webmati restore docs/setup.md --at 12 # that file back here as #12 held it — then push
webmati files restore 12 # EVERY file back on the server as #12 held them (asks first)
webmati files ls app/src # the files the server keeps under app/src
#14 2026-10-11 10:02Z lan@shop.vn · webmati-cli@lan-mac "step two"
modified docs/setup.md
restore --at only writes your copy: status shows the file modified and the
next push sends it, like any edit. files restore changes the server for
everyone (webmati pull brings it here); it is recorded as a snapshot itself,
so it can be undone the same way, and if someone pushed between its plan and the
restore it is refused and nothing changes.
history backend/workflows/<code> lists the versions a workflow was published as
(webmati tool call revert_backend_workflow puts one back live). Theme files keep
their history as theme versions in the admin (whole-theme copies);
history covers the project's own files and workflows.
Limits
| one file | 10 MB |
| every file together | 200 MB |
| files | 5000 |
A bigger file is reported by status and never sent: leave it out with
.webmatiignore (build output and dependencies never need syncing).

