Skip to content

Project & workspace

Two words, two scopes, and they are easy to confuse.

  • A project is what you open in the desktop app: a folder on your machine holding collections, chains, auth providers, and the rest.
  • A workspace is the unit of sharing and sync in the paid cloud tier.

Locally, you only ever deal with projects. The cloud tier is not available to sign up for at v0.1.0-preview.10 — see Local & cloud.

Point Tinspec at a folder and that folder is the project. Everything is plain YAML, laid out as in Project layout, and meant to be committed alongside the service it tests.

You can also create a folderless project, which writes nothing to disk. It is fine for a scratch session, but run history, the spec cache, and endpoint overlays all need a folder, so they are session-only there.

The tinspec CLI finds a project the way git finds a repository: the nearest ancestor directory containing a .tinspec/ folder. Same layout, same files, no export step.

This is a hard rule, not a preference:

The YAML files are authoritative. Collections, chains, auth definitions, providers, route matchers, overlays — all of them are files, all of them diff cleanly, all of them are yours.

SQLite is a rebuildable cache. Each project folder gets a .tinspec/tinspec.db holding run history, the spec cache, and search indexes. Delete it and everything is rebuilt by re-reading the files. The only thing genuinely lost is run history.

Secrets are in neither. Not in the files, not in the database. They live in your OS keychain or a machine-local encrypted store, and the files reference them by name.

The consequence is that your API tests are just source code. They live in the repository of the service they test, they review in a merge request, they branch when the API branches.

Two folders under .tinspec/ hold plain, committable .js:

  • .tinspec/lib/ — reusable snippets. The Scripts tab inserts one at the caret, and the inserted text is what gets committed with the request, so the collection stays self-contained.
  • .tinspec/functions/ — standalone scripts you run for a one-off computation: mint a signature, decode a token, derive a value into a variable.

Neither is resolved at run time, because there is no module loader and no require — a collection gets pulled from a teammate and executed against production credentials, and require would turn “read this collection’s scripts” into “audit a dependency tree fetched at run time”. What runs on a send is always the inline hook in the YAML: exactly the text a reviewer reads.

A generated .tinspec/tinspec.d.ts describes the ts.* surface so an external editor offers the same completions the in-app one does.

Settings have layers, and the collection is one of them

Section titled “Settings have layers, and the collection is one of them”

A collection’s defaults block is not only headers and variables. defaults.settings is the collection/folder layer of the layered send settings — a timeout, a proxy, a CA bundle for every request in that file — and defaults.scripts is the collection layer of the pre/post hooks, inherited field by field.

Layers resolve most-specific-first (request, then collection, then API provider, then the app’s global options), and the engine reports which layer each value came from rather than flattening it away.

The format is versioned, and that is now enforced

Section titled “The format is versioned, and that is now enforced”

A collection file carries formatVersion, and a chain file carries its own. For a while both were parsed and round-tripped but never actually checked — which meant a build that met a file written by a newer one simply hard-errored, on a format people commit to git.

At v0.1.0-preview.10 loading goes through one seam that returns the document, the version it declared, and a list of warnings:

  • An older file is migrated to the current version and stamped, so a re-save no longer claims the old one. Every step is explicit; an unknown older version is a clear error rather than a guess.
  • A newer file is still read. Fields this build does not model are pruned and reported — “written by a newer Tinspec; unknown fields were ignored: …” — rather than causing a failure.
  • But saving a newer file is refused, deliberately, instead of being quietly lossy. Re-injecting the ignored parts is unsound the moment a list item is added, removed or reordered, so the newer file stays byte-intact on disk and fully readable.
  • At or below the known version nothing is relaxed. A misspelled key is still a hard error, with its line and column.

Nothing users see changed numbers in this release: a collection is still formatVersion: 2 and a chain still formatVersion: 3. Every addition was additive, so an existing collection round-trips byte-identically.

In the cloud tier a workspace is what a team shares. Sync is server-as-source-of-truth, versioned whole-document push and pull: each document carries a version, a push from a stale base returns a conflict rather than overwriting, and a pull never silently overwrites a local file you have edited since it last synced — that is a conflict too, which you resolve by taking remote, keeping local, or deleting.

Secrets never sync. Auth definitions reference secret names; the values stay on the machine that holds them.

Endpoint overlays do not sync either — there is no overlay document kind on the server, so an edit to a generated endpoint stays on the machine that made it unless the file travels through git.

The desktop app remains fully usable offline with no account, and nothing in these docs outside the Local & cloud page requires one.