Archestack in VS Code
With the Archestack extension for Visual Studio Code, your app is a git repository and runs on your own machine. Open the repository's folder and the extension starts a local Archestack for it: PostgreSQL, the API and the builder, from the runtime the extension ships with. There is no workspace to connect to and nothing else to install. Script modules, business events, pages, templates and tables are files in the folder, and every file you save reaches the local Archestack a moment later.
That local Archestack is called the project. It belongs to the repository: a teammate who clones it gets the same project, made from the same files. Working as a team covers how a larger company's developers share one repository, and The VS Code extension in detail shows every screen.
Install
The extension is not in the Visual Studio Marketplace yet. It comes as a VSIX file: in VS Code, open the Command Palette, run Extensions: Install from VSIX... and pick the file. It needs VS Code 1.90 or later.
Use the Windows build (x64). It carries the Archestack runtime, the API, the builder and PostgreSQL, so a project runs with nothing else installed. PDFs render in the Edge or Chrome already on the machine. The universal build has no runtime and runs projects in Docker Desktop instead.
The extension does not need Microsoft's C# extension or the C# Dev Kit. Its C# features come from the project itself, and its scripts open in a language of their own, so either can stay installed without the two getting in each other's way.
Start a project
- A new app. Open an empty folder, open the Archestack view in
the activity bar and choose Start project. The extension adds
archestack.json, creates a fresh database, starts the API and the builder, and signs in as the project's admin by itself. New Package then starts the first package folder. - An existing repository. Clone it and open its folder. The window starts the project on its own: the first start creates the database and loads the repository in, every package's tables first and then the artifacts, dependencies before the packages that need them. A fresh clone is a working app in about fifteen seconds, and reopening the window picks the project up again.
The project is described in archestack.json, which is committed:
"project": {
"name": "archestack-sales",
"engine": "native",
"webPort": 5310,
"dbPort": 5312,
"adminEmail": "admin@archestack.local"
}
The builder and the API share webPort and PostgreSQL listens on dbPort,
both on 127.0.0.1 only. The project's database, its generated passwords and its logs live in
.archestack/project/, which git ignores. The NuGet packages its scripts download are
kept there too, in .archestack/project/nuget, or in a Docker volume named
nuget when the project runs in Docker, so a restart does not download them again.
The status bar shows the project as local. Its list opens the project's builder in
the browser, stops the project and shows the API's logs. Reset Project Database
throws the project's data away and loads the repository in again.
The project belongs to the window running it. Closing or reloading the window stops the API and PostgreSQL, so nothing is left running, and the next window starts them again in seconds. If the API stops answering while you work, the window starts it again within ten seconds.
The repository
A repository holds one folder per package, and one or more files per artifact:
archestack.json the environments this repository knows, and its packages
packages/sales/
package.json name, version, dependencies
nuget.json the NuGet packages its scripts use, each at an exact version
tables/orders.json one per table: columns, keys, indexes, foreign keys
layout.json where the tables sit on the diagram
entities/Order.json
scripts/Calc.csx + Calc.json a script module: its code, and its parameters
code/Sales/OrderSummary.cs shared code, named by its path: C# every script can use,
code/components/StatusChip.tsx and modules pages import from @/
triggers/Order/OnCreate.json + OnCreate.1.csx
pages/Orders/page.json + overview.tsx + OrderRow.tsx a page's own files beside its views
templates/Card/template.json + template.tsx
pdf/Invoice/template.json + template.html + data.csx
schedules/nightly.json
tests/smoke/suite.json + cases/doubles.json + doubles.csx
api/customers.json a Data API endpoint, named by its route, every version inside
workspace/
branding.json, logo.png, layouts.json References between artifacts are names, never database ids. Nothing that is decided per environment is in the files: who may see a page, whether anything is public, Data API grants and run history stay with each environment. The same content always gives the same bytes, so git shows only real changes.
The project is fed the folder as you save. A saved change under
packages/ reaches it a moment later, and what you delete in the folder is deleted
there, a whole package folder included. There is nothing to push. Tables are the exception: they
change through migrations.
New Package starts a package folder, and New Artifact in
Package (right-click anything under packages/ in the Explorer, or
+ on a package in the Workspace view) writes a new artifact's files, asking only
for what it cannot exist without, such as the entity a business event belongs to. A new page is a
code page. A new shared code file asks for its path, and its extension says what it is.
Add File on an artifact in the Workspace view adds a file beside it: a page gets
a file of its own next to its views, which import it as ./Row, and a script module,
business event or other script, which is one file, gets a shared code file in the same package,
in a folder named after it, that every script can use.
The project's builder works too, in the browser. A change made there is not in the folder until you bring it in with Pull Package to Folder, and a package changed in both places is left alone and said once, so nothing is overwritten. Commit what you pull like any other change.
Edit artifacts
An artifact's code is a file, and its settings are the JSON beside it, which opens in a form. Every change in the form is written into the file; Edit as JSON opens the text.
| Artifact | Code files | Settings form |
|---|---|---|
| Script modules | scripts/Name.csx | Description, parameters, admin only |
| Business events | Name.1.csx, Name.2.csx, one per script action | Entity, when it fires, the condition, the actions |
| Shared code | The file itself, under code/: .cs for C#, .tsx, .ts, .jsx or .js for a module | None |
| Pages | A .tsx for each view that is code: overview, detail, create, and the page's own files beside them | Route, menu, publishing, which views are code |
| PDF templates | The HTML, the data script, the header and footer | Paper, margins, scale, parameters |
| Frontend templates | template.tsx | Description, parameters |
| Scheduled events | A .csx for a script schedule | Cron, and the business event or script it runs |
| Test suites and cases | A .csx for a script case, a file per code check | Given rows, what runs, Then checks |
| Business entities | None | The entity editor: master table, joins, columns, labels |
| Data API endpoints | None: api/route.json is the whole endpoint | The entity it serves or the script module it calls, its route, and each version's status with its operations and fields, or its method and parameters |
A business entity opens in the entity editor. Its tables and columns are picked from the project's
database, so a name that does not exist is flagged before you save. The workspace's own settings
are files too, in workspace/: the branding with its logos, and the published app's
layouts, each opening in a form.
C# scripts
Script modules, business event scripts, PDF data scripts, scheduled events and test scripts get the C# features of a .NET editor. They are answered by the project's own compiler, so the editor sees exactly what the script sees when it runs:
- Completions, with each member's signature and documentation.
- Hover, and parameter info while you type a call.
- Errors and warnings as you type.
- Go to Definition (F12). On a script module's method it opens that module's script, on a parameter the settings that declare it, and on anything else its declarations, read only.
- Format Document, in the .NET style.
The editor knows the real script globals (Db, Entity,
Modules, Mail, Notify, Pdf, Ocr,
Doc and Log), a module's parameters as typed variables, and in a
business event, the entity's columns on Entity. Business entities are typed sets on
Db and script modules are typed methods on Modules, so a renamed
column or a wrong argument is an error on its line before anything runs:
// A script module is a typed method: a wrong name or argument is an error as you type
var total = await Modules.Calc(qty: 2);
// Every business entity is a typed set
var open = await Db.Order
.Where(o => o.Status == "Open")
.ToListAsync(); Business entities as typed sets in the reference explains how the names are made.
Types more than one script needs, such as records and helpers, go in a shared C# file under a
package's code/ folder. It is ordinary C#, gets the same features, and F12 on one
of its types in a script opens it. Shared Code in the
reference explains what a shared file can use.
Libraries from NuGet are declared in the package's nuget.json, each at an exact
version, and their types complete like the rest once the workspace has downloaded them. See
NuGet Packages in the reference.
Test a script module
Test, above a script module's code, runs the module the way the builder's Test
tab does, from the script as the editor has it, saved or not. It is also on the editor's title
bar, on the module in the Workspace view and on its form. A panel beside the script asks for the
parameters, checks each value against its type and remembers them for next time. A module that
needs no value it does not have runs at once, and Ctrl+Enter in the panel runs it again. The panel
shows whether it passed, what it returned and what it wrote to Log, and on a failure
the error with a button to its line.
A test is a dry run: writes through Db and Files are listed rather than
made, and mail and notifications rather than sent, also those of the modules it calls. Anything
else the script does itself is real: HTTP calls, System.IO, and anything a NuGet
package does, such as a connection to another database. Tick
Show the SQL it ran to see its statements in the SQL Trace panel. Only
admins and owners can test. A test says what one run does; a check that runs every time is a test
case, see Tests.
Page and template code
A page's code and a frontend template's .tsx are checked against what that code
really has when it runs: backend, pageContext,
currentUser, the hooks, React, every MUI component with its props, the icons, and a
row type for each business entity, such as CustomerRow. Completions, hover, errors,
go to definition, rename and Format Document all work, and a top-level
return is fine. It runs on the TypeScript that comes with VS Code, so nothing needs
installing.
Code can be spread over files. A page's own files sit in its folder beside the views and are
imported as ./OrderRow; code more than one page uses goes in a package's
code/ folder and is imported from @/, from any package of the
repository. Imports are checked and typed, go to definition follows them, and a rename changes
every file that uses the name.
What page code can use, the link above every page's and template's code, opens the full list beside it: every global with what it does and an example, and the project's row types. The builder shows the same list.
Previews
Open Preview to the Side, the button at the top right of a PDF template, a frontend template, a page's code or an app layout, shows it as it stands in the editor, saved or not, and follows every edit:
- A PDF template is rendered by the project. Its data script runs as a dry run, so a preview never writes a row or sends a mail. Parameter values can be changed at the top.
- A frontend template or a page is rendered by the project's own app, with the template's parameters or the page's record id at the top, and with the page's own files and the repository's shared code as their editors hold them. A page is drawn in the app layout the published app puts around it; the toolbar switches to another layout or to none.
- An app layout is drawn by the published app's own shell, for a page you pick.
- A template, page or layout can be shown on a phone, a tablet or a desktop at its real size, turned to landscape or at a size of your own, and zoomed to fit or to a percentage. The next preview opens on the device you picked last.
Tables
A package's tables are files, packages/<package>/tables/<table>.json,
one per table. They open in the table editor: columns with their types, keys, defaults and foreign
keys, and the table's indexes. The Database view lists the project's tables;
Open Database Diagram draws them with their foreign keys, and dragging a table
there writes the package's layout.json. New Table asks which
package the table belongs to.
Saving a table file does not change the project's database. The rest of the folder goes in, and the Workspace view and a message say which table changes wait for a migration. A fresh project database, the first start or a reset, gets the folder's tables by itself.
Migrations and rollback
Add Migration plans the table changes against the project's database and shows
every operation, its risk and the exact SQL. Operations that lose data, such as dropping a table
or a column or narrowing a type, need Allow data loss ticked. The review also
lists the typed scripts the change would stop compiling. The migration is stored as a pending
deployment with a name, suggested from what changes, such as AddTechnicianSkill;
Deploy runs it, and Add and Deploy does both at once.
The Deployments view is the history, newest first. Show SQL shows what a deployment runs. Roll Back to Here plans taking the tables back to what an earlier deployment made, and adds that as a pending deployment of its own, to review and deploy like any migration. A rollback restores structure, never data: the rows in a table or column it drops are gone.
Drop Table removes the table's file and marks the table; it goes when a migration with data loss allowed is deployed.
SQL console
New SQL Query opens an SQL editor on the project's database, with table and column
completion. Run Query (Ctrl+Enter) runs the selection, or the whole file,
statement by statement, each committed on its own, and stops at the first error. Results open
beside the editor, up to 1,000 rows each, and copy as CSV. Nothing asks before a
DELETE.
Query plans and SQL traces
Explain Query shows the plan PostgreSQL picks for the selection, or for the
statement the cursor is in: every step, what each one reads and how many rows it expects.
Explain Analyze Query (Ctrl+Alt+Enter) runs the statement for actual times and
rows, in a transaction that is rolled back, so even a DELETE keeps its rows. The
plan marks the step that takes the most time and what is worth a second look: a scan that reads
thousands of rows to keep a few, a row estimate far from what came back, a sort that spilled to
disk.
Trace SQL shows the SQL Archestack itself sends. Right-click a script module or a business entity in the Workspace view, or use the button on its file or form. A script module runs with the parameter values you give, as a dry run by default, whose writes are recorded rather than made. A business entity is read the way a page's grid reads it. The SQL Trace panel then lists every statement with its values, how long it took and the code that sent it, and can put the same statement together to show a query that runs once per row. Click a statement to explain it, open it as SQL with its values in place, or copy it. In the Testing view, Run with SQL Trace does the same for test cases. Only admins and owners can trace.
Tests
Run Tests in the Workspace view runs every package's tests, one package's, a suite or a single case. The folder is fed to the project first, so a case exercises the scripts and pages as they are in the folder. Each case runs in a test sandbox, a database of its own, never against the project's data, and mail and notifications are recorded rather than sent. Four cases run at once. Every case shows its latest result and how long it took in the tree; clicking it shows what failed, expected against actual, and what it printed. The Testing view lists the same cases.
A check that one subject and one operator cannot say is a code check: a C# file of its own that
runs after the case, with Result, Output, Error and
Failed at hand, and fails when it throws, fails an Assert or returns
false.
Event Log
The Event Log view shows what ran in the project, grouped by kind: business events, script modules, scheduled events, test runs, mail and backups, each with the last day's runs and failures. A new failure is counted on the view's badge and announced as it happens. Click an entry to read everything it recorded. Open Artifact opens the file of the script it failed in, on the failing line.
Data API
A Data API endpoint serves a business entity to other systems,
at an address like /api/data/v1/customers. It belongs to a package like any other
artifact: one file, api/customers.json, named after its route and holding every
version of it. The file opens in a form: the entity it serves, its name and route, and per version
its status, sunset day, page size, operations and fields. Each field is a public name over one of
the entity's columns, returned, writable or required; the form offers the columns and flags a field
whose column the entity lacks.
An endpoint can call a script module instead, for work that is more than reading or writing rows. Each version names the module and the one method it answers, POST with the parameters in a JSON body or GET with them in the query string, and lists the parameters callers send: a public name over one of the module's parameters, required or not, with a description. The form offers the module's parameters and flags one the module needs that the version does not pass. A caller gets what the module returns, as JSON. A module marked admin only stays that way: only admins and owners may call it, so its endpoint takes no grants.
A version that has been published only grows. Adding fields and operations is fine; removing or renaming a field, taking its write access away, making it required or dropping an operation would break the systems calling it, so it goes into a new version, which the form starts as a copy of the one shown. For an endpoint that calls a script module, the version keeps its module and method, and gains only optional parameters. A save that breaks a published version is refused and says why. Activating a version offers to deprecate the older ones: they stay served, telling callers in their headers that they are deprecated and, once you set one, their sunset day, until you retire them.
The Data API view lists the project's endpoints with their versions, and its API clients. Try It on an endpoint or a version sends real requests and shows the answer as it came, headers included: as you, with the SQL it ran if you ask, or with an API client's key, to see exactly what that client may call. On an endpoint that calls a script module it sends the call, with sample parameters. The view's buttons open the OpenAPI document of everything you may call and How to connect, the guide for the people building on it.
Who may call an endpoint is decided per environment and is never in the file. The form's Edit access in the workspace opens the grants: which roles may list, get, create, update or delete, or call the module. An API client is a service account with roles and a key instead of a password. New API Client creates one and shows its key once; Rotate Key replaces it, and the old key stops working at once.
Coding agents
The repository tells coding agents such as Claude Code and Codex what they can make in it, the way the MCP server tells a connected model. New Package and every pull write:
-
AGENTS.md: how a change reaches the app, the rules every file follows, the mistakes to avoid and an example of every kind of artifact. Only the extension's marked section is rewritten, so your team's own notes in the file stay. CLAUDE.md, which points Claude Code atAGENTS.md.-
docs/archestack/: the script and test guides and what page code can use. It is committed, so an agent working on a fresh clone has them too. -
.archestack/context/: the project's business entities and its generated data model, for this machine only.
Update Agent Instructions writes them again. An agent's edits are saved files like yours, so they reach the project the same way; a person reviews and commits them.
Working as a team
For a larger company the repository is the application's source of truth, the way infrastructure as code describes servers: everything that makes up the app is a file in git, reviewed and versioned like any other code, and every environment is made from those files. The extension is how each developer works on them.
Every developer runs their own copy
Each developer clones the repository and opens it, and their window runs a project of its own, with its own database, on their own machine. Nobody shares a development workspace, so nobody saves over somebody else's work, and a script that breaks stays on one machine until it is fixed. A new team member has the app running about fifteen seconds after the clone. A project's tables start empty, and test cases write the rows they need into their own sandbox, so every developer tests against the same data.
One package per team
A large app is several packages, each a folder with its own tables, business entities, pages, scripts and tests. Split them the way the company is split, and teams mostly work in folders of their own:
packages/
core/ customers, products, shared scripts team Platform
sales/ quotes and orders, needs core team Sales
planning/ work orders and schedules, needs core team Operations
invoicing/ invoices and PDF templates, needs sales team Finance A package names the packages it needs in its package.json, by name and version range:
{
"formatVersion": 1,
"name": "invoicing",
"version": "1.4.0",
"description": "Invoices and their PDFs, made from sales orders.",
"dependencies": [
{ "name": "sales", "versionRange": "[2.0,3.0)" }
],
"masterData": []
}
A bare version such as 2.0 means that version or newer, [2.0] exactly
that one, and [2.0,3.0) anything from 2.0 up to, but not including, 3.0. A project
loads dependencies before the packages that need them. A table another package owns is carried
as reference only: a package can point a foreign key at it, but never changes it. Your git host's
code owners feature can then require a review from the owning team for each folder.
Or a repository per team
Teams that should also release on their own schedule can each own a repository instead, and with it an app, a project and environments of their own. The data one team owns and another needs reaches the other as third-party tables: its environments connect to the owning team's database and mirror the tables they need, on a schedule. A mirrored table reads like any other table of the app, in business entities, pages and scripts.
sales-app/ team Sales owns customers, quotes and orders
planning-app/ team Operations mirrors customers from sales-app
invoicing-app/ team Finance mirrors customers and orders from sales-app - One owner per table. A mirrored table is changed in the app that owns it; the others read their copy. Columns marked as managed by Archestack are the exception: they hold a team's own additions, and a sync never overwrites them.
- As fresh as the last sync. A table syncs in full or by what changed, which needs
a column saying when a row last changed, such as
modified_at. - Connections belong to the environment. They are not in the files, so each environment connects to its own counterpart: acceptance to acceptance, production to production.
One repository with a package per team deploys as one app, and a package can point a foreign key at another package's table. A repository per team lets every team release on its own, and pays for that with copies that are only as fresh as their last sync.
Branches and reviews
- Work on a branch and open a pull request, as with any code. Every reference is a name and the same content always gives the same bytes, so a diff shows only what someone changed: a line of a script, a column, a page's JSX, never a renumbered id or reordered keys.
- Checking out another branch changes the files, and the project follows them as it follows your saves. Table changes then wait for a migration; Reset Project Database starts over from the branch's tables instead.
- A reviewer checks out the branch, has it running in their own project, and runs the package's tests against it with Run Tests. Tests live in the package, so they travel with the code they test.
-
AGENTS.mdanddocs/archestack/are committed, so every developer's coding agent follows the same rules.
Tables in a team
A table file describes the structure a table should have, not the steps to get there. After a pull or a merge, each developer's project plans its own migration from the table files with Add Migration, and the review shows exactly what that database will run, data loss and typed scripts that would stop compiling included. Every tool writes a table file in one canonical form, so two people's edits to the same table only conflict where they change the same thing.
What each environment decides
Who may see a page, what is public, Data API grants and API clients, users and roles, mail accounts, connections to other systems and run history are not in the files. Each environment's administrators set them, so a merge can never make something public or give anyone access.
Test, acceptance and production are deployed from the repository by a pipeline, as Deployment strategy describes.
Settings
| Setting | Default | What it does |
|---|---|---|
archestack.project.startOnOpen | On | Run the repository's project when a window opens on it. |
archestack.project.syncOnSave | On | Feed saved changes under packages/ into the project. |
archestack.project.runtimePath | Empty | A runtime folder to run the project from, instead of the one the extension ships with. |
archestack.tests.parallel | 4 | How many test cases run at once, 1 to 8. |
archestack.eventLog.notifyFailures | On | Announce failures in the project. The badge counts them either way. |
archestack.diagnostics.enabled | On | Compile open C# scripts and show their errors and warnings. |
archestack.diagnostics.delayMs | 600 | How long to wait after the last keystroke before compiling, in milliseconds. |
Bringing in an existing package
A package that so far lives only in a hosted workspace comes into the repository once: run Connect to Workspace from the Command Palette, sign in, use Pull Package to Folder on the package, and pick the project again in the status bar. From then on the folder is the source and the project runs it. The package's tables come along, with the tables it points a foreign key at marked as reference only, so the project can make every table the package needs. In a project that already has a database, those tables wait for a migration like any other table change.
Gotchas
- A project starts with empty tables. The folder carries the structure, not the rows. Loading master data from the repository is not built yet.
- Tables never change on save. Only a deployed migration changes the project's database, apart from a fresh start or a reset.
- A reset throws the project's data away. Everything else is in the folder and comes back; rows you entered do not.
- Builder changes stay in the project until you pull them into the folder, and a reset loses them.
Where this goes next
Workspaces and hosting explains where your app runs once it leaves your machine. The reference covers packages, script modules and database deployments in more depth.