Relatude Help

12.2 What goes where: source control, deployment and appsettings

An application on Relatude.DB is configured from four places: the code, the settings files in relatude.settings/ (SHARED), the configuration of each environment (appsettings*.json, environment variables, secrets), and the data folder of each installation (THIS SERVER). Three rules decide what goes where:

  • What describes the application is committed to source control and deployed with the code.
  • What an installation changed or recorded stays with that installation. It is never committed, and a deployment neither carries it nor overwrites it.
  • What differs between environments comes from configuration, and so does every secret.

In short: commit relatude.settings/. Never commit relatude.data/ or relatude.data.temp/, and exclude both from the project file so that a publish does not deploy them either (Publishing).

The folders. With the default ServerOptions, a project looks like this. The content root is the project folder while you develop, and the folder the application is deployed to on a server.

MyApp/                                 content root and root data folder
├── Program.cs, Models/, …             the code                              committed
├── appsettings.json                   configuration for every environment   committed, no secrets
├── appsettings.Development.json       what Development changes              committed
├── appsettings.Production.json        what Production changes               committed, no secrets
├── relatude.settings/                 SHARED: what the application says     committed, deployed
│   ├── relatude.db.json               databases, storage, index engines, model sources
│   ├── <FileKey>.json                 custom settings
│   ├── datamodel.json                 datamodel overrides (§3.1)
│   ├── logs/                          log definitions, one <key>.json per log (§32.1)
│   ├── graphql/                       GraphQL endpoints, one <name>.json each (§33.2)
│   └── <ShortName>/                   the same, per database with a short name
├── relatude.data/                     THIS SERVER: this installation        never committed or deployed
│   ├── data/ state/ backup/ logs/ …   transaction log, state, backups, log entries, indexes, files
│   ├── overrides/                     what the admin UI changed here
│   │   ├── relatude.db.overrides.json   settings (default database only)
│   │   ├── <FileKey>.overrides.json     custom settings
│   │   ├── datamodel.overrides.json     datamodel overrides
│   │   └── logs/  graphql/              log definitions and endpoints added, changed or removed here
│   ├── datamodels/                    the model editor's draft and model history
│   ├── modelsources/                  model files of RuntimeTypes sources without a Filepath
│   └── installation/                  this installation's id
└── relatude.data.temp/                scratch, emptied at every start       never committed or deployed

Other databases keep the folders from data/ to datamodels/ in their own storage. relatude.settings/ sits in the root data folder, so ServerOptions.DefaultDataFolderPath moves it too. A database's SHARED files are found by its short name, so set each database's ShortName in relatude.db.json, where every installation reads it (§12.1).

At every start, THIS SERVER is merged over SHARED. Logs and GraphQL endpoints are replaced whole: a definition on THIS SERVER replaces the SHARED one with the same key, and one removed on THIS SERVER is gone on that installation only.

While you develop

dotnet run and Visual Studio start the application with the project folder as its content root. So the relatude.settings/ the application reads is the folder in source control, and what the admin UI writes into it is ready to commit:

  • Settings changed on the settings pages go to the overrides file in relatude.data/, outside source control. That is the place to try a value out. To keep a change, choose Move to shared → Move into relatude.db.json on the settings page, and commit the file.
  • Model changes in the model editor are written into the model source where it can be written: the model files of a RuntimeTypes source, or the generated C# files of a CompiledTypes source (rebuild and restart to see them). Otherwise they are saved as overrides on THIS SERVER, and Move to shared in the model editor moves them into relatude.settings/…/datamodel.json. Commit that.
  • Log definitions and GraphQL endpoints made in the admin UI go to relatude.data/overrides/. Move to shared on the Logs and API pages moves them into relatude.settings/…/logs/ and …/graphql/. Commit those.
  • A RuntimeTypes source without a Filepath keeps its model files in relatude.data/modelsources/, inside the data folder. For a model that belongs to the application, give the source a Filepath in the project, such as "Models/Json", and commit that folder.

The .gitignore needs only two entries for Relatude. relatude.settings/ is committed:

relatude.data/
relatude.data.temp/

A complete .gitignore for a typical project, with build output, client builds and secret files too, is in the documentation. Put it in the root of the repository; its patterns match at any depth. The project types of relatude new come with their own. A .gitignore does not stop a publish from copying the data folder (below).

Secrets do not go into relatude.db.json. relatude new and relatude init write a random TokenEncryptionSecret into the file, and the admin password when given --password, so you can log in to a new project at once. Before you share the repository, move both into user secrets (below) and remove them from the file.

Publishing

The Web SDK copies every .json file in the project folder to the publish folder. That is how relatude.settings/ and the appsettings files reach a server, but it would also deploy your machine's data folder over the server's. So the project file leaves the data folder out, as the project types of relatude new do:

<PropertyGroup>
  <DefaultItemExcludes>$(DefaultItemExcludes);relatude.data\**;relatude.data.temp\**</DefaultItemExcludes>
</PropertyGroup>

Check the publish folder before the first deployment: relatude.settings/relatude.db.json must be there, and relatude.data/ must not. A server that finds no settings file creates one that uses the bundled demo model. So a database full of Relatude.DB.Demo.Models types means the deployment left relatude.settings/ behind.

On a server

A deployment leaves the data alone. It replaces the code, relatude.settings/ and the appsettings files, but nothing in the data folder, which is relatude.data/ in the application's folder by default. That works on a virtual machine, under IIS or on Azure App Service, as long as the deployment never deletes files it did not put there: turn off Remove additional files at destination, and keep relatude.data/ out of an rsync --delete. Two other setups are common:

  • In a container, mount a volume at relatude.data/ below the content root. The image carries the code and relatude.settings/, and the volume carries the installation.
  • On Azure Blob storage (an IOSettings entry of type AzureBlobStorage for IoDatabase), the database and its overrides/ live in the blob container, and the application's folder holds only what was deployed. Use this with deployment slots: a swap exchanges the slots' folders, so a database kept in the folder would change places with the code.

Admin UI changes stay with the installation. Settings, datamodel overrides, log definitions and GraphQL endpoints changed on a server are kept on THIS SERVER, in the data folder. They survive every deployment, and each deployment's SHARED files still apply beneath them.

A move to SHARED on a server does not last. Every Move to shared writes into relatude.settings/, and so does activating a model change in a RuntimeTypes source whose Filepath is in the application's folder. On a server those files are the deployed copy, which the next deployment (or the next container) replaces. The moved entries have left the overrides file too, so they are lost. The dialogs warn about this outside the Development environment. Bring a change into the application through source control instead:

Changed on a server To make it part of the application
A setting Copy the value from the Move to shared list into relatude.db.json in source control, and deploy. Once the deployed file agrees, the next save drops the entry.
A datamodel override In the model editor's Move to shared dialog, Download shared file gives a datamodel.json with the chosen overrides, without changing the server. Commit it and deploy.
A log definition The log's Definition view shows its file. Copy it into relatude.settings/…/logs/ and deploy. Entries stay in the data folder, so a definition with the same key picks them up.
A GraphQL endpoint Copy the endpoint's file into relatude.settings/…/graphql/ and deploy, or map the endpoint in code.
A model in a RuntimeTypes source Copy the files from the source's Filepath into source control before the next deployment, or make the change in development instead.

A restored data folder brings its changes along. Production's relatude.data/ restored on a test server brings production's settings, datamodel overrides, log definitions and GraphQL endpoints with it, on THIS SERVER. Look through the Move to shared dialogs after such a restore.

appsettings, environment variables and secrets

The RelatudeDB configuration section (above) lets one relatude.db.json serve every environment. ASP.NET reads configuration in this order, a later source winning: appsettings.json, appsettings.{Environment}.json, user secrets (in Development only), environment variables, the command line. The environment is Production unless ASPNETCORE_ENVIRONMENT says otherwise; dotnet run and Visual Studio set Development.

A split that works:

Where What goes there
relatude.db.json What every environment shares: the databases and their ids, storage backends without connection strings, file stores, index engines, model sources, engine defaults.
appsettings.json Relatude values every environment shares that you prefer to keep with the rest of the configuration. Often nothing.
appsettings.{Environment}.json What one environment does differently: cache sizes, backups, AllowMasterLoginOutsideLocalhost, another blob container.
User secrets A developer machine's secrets: the admin password, TokenEncryptionSecret, API keys, a storage connection string. They live in your user profile, outside the project folder.
Environment variables, a vault A server's secrets: App Service application settings, a container's environment, Key Vault references.
// appsettings.Production.json: committed, no secrets
{
  "RelatudeDB": {
    "AllowMasterLoginOutsideLocalhost": true,
    "ContainerSettings": [
      { "Id": "8f6b…", "LocalSettings": { "NodeCacheSizeGb": 4, "AutoBackUp": true } }
    ]
  }
}
# a developer's machine ("init" adds a UserSecretsId to the project file the first time)
dotnet user-secrets init
dotnet user-secrets set "RelatudeDB:MasterPassword" "…"
dotnet user-secrets set "RelatudeDB:TokenEncryptionSecret" "…"
# a server: environment variables, or application settings on Azure App Service
RelatudeDB__MasterPassword=…
RelatudeDB__TokenEncryptionSecret=…
RelatudeDB__ContainerSettings__0__Id=8f6b…
RelatudeDB__ContainerSettings__0__IOSettings__0__Id=9d8c…
RelatudeDB__ContainerSettings__0__IOSettings__0__BlobConnectionString=…

Environment variables separate the levels with __, where user secrets use :. The numbers are positions in the configuration's own lists. Give each element's Id beside them, as above, and the element is matched by id to the one in relatude.db.json, not by position. Then reordering the file never sends a connection string to the wrong backend.

A few things to know:

  • An empty value is a value. "ApiKey": "" in an appsettings file blanks the setting. Leave the key out instead; a // comment can say where the real value comes from.
  • Configuration cannot remove anything or set a value to null. Which databases and storage backends exist belongs in relatude.db.json.
  • The command line tool reads the same files. relatude settings --environment Production shows the settings a Production server would run with, from the appsettings files and the shell's environment variables, but not user secrets. It reports each secret as set or not set, never its value (§31).