Eternaltwin Integration
This section describes how to integrate Eternaltwin into your project's repository. Integrating Eternaltwin to your repository allows you to run and test your project using a locally installed version of the Eternaltwin website.
Eternaltwin is installed as a project-local package (a Node package, or a Composer package for PHP projects):
- it ensures all the contributors use the same version
- the project is fully self-contained and does not require an internet connection to run
- if you have multiple projects on your computer, there are no conflicts: each one has its own Eternaltwin version.
The packaged version is not the full website (for example, it does not include translations). It's a lightweight version specifically intended to be installed inside other projects.
The current published version is 0.16.4.
System requirements
You need the following tools on your system:
- Node.js: version
20.11.0or higher (this is the version Eternaltwin itself is developed against; some of the published packages still declare a lowerengines.nodefloor, but they are not tested below it) - Yarn
The package ships a precompiled Eternaltwin executable for the following platforms only:
x86_64Linuxx86_64Windowsx86_64macOS
On any other platform (in particular aarch64/Apple Silicon and ARM Linux),
no precompiled executable is available: the install succeeds but the first call
fails with native eternaltwin executable not found. You then need
Rust and a local build of the executable, forced with the
ETERNALTWIN_FORCE_BUILD=true environment variable.
ℹ Using npm as an alternative to yarn is not officially supported but should work.
Configure your repository for Node packages
Your repository must contain a package.json file at its root. It is a
manifest file containing metadata for Node.js.
If your project does not have a package.json, you may create one by running
the following command at the repo root and replying to the prompts:
yarn init
(add -p if your project is private and should never be published).
You may read the Yarn manifest
reference if you wish to learn
more about package.json files. The conventions used inside the Eternaltwin
repository itself are described in package.json.
Below is an example minimal package.json file.
{
"name": "myproject",
"version": "0.0.1",
"licenses": [
{
"type": "AGPL-3.0-or-later",
"url": "https://spdx.org/licenses/AGPL-3.0-or-later.html"
}
],
"private": true,
"scripts": {},
"dependencies": {},
"devDependencies": {}
}
Make sure to commit the package.json file.
Install Eternaltwin inside your project
Run the following command in the directory containing package.json:
yarn add --dev @eternaltwin/cli
This will perform the following 3 actions:
- Update your
package.jsonfile to document the new dependency on the package@eternaltwin/cli. - Download the package (and its own dependencies) into the
node_modulesdirectory. - Create (or update) a
yarn.lockfile to remember the exact version of the dependencies that were installed and prevent accidental regressions.
@eternaltwin/cli is a thin launcher: it depends on @eternaltwin/exe, which
provides the native Eternaltwin executable for your platform, and forwards
every argument to it.
Commit the package.json and yarn.lock files.
Do not commit the node_modules directory: add the node_modules/ rule to your .gitignore file.
The resulting package.json should be similar to:
{
"name": "myproject",
"version": "0.0.1",
"licenses": [
{
"type": "AGPL-3.0-or-later",
"url": "https://spdx.org/licenses/AGPL-3.0-or-later.html"
}
],
"private": true,
"scripts": {},
"dependencies": {},
"devDependencies": {
"@eternaltwin/cli": "^0.16.4"
}
}
Yarn exposes the binaries of your dependencies directly, so you do not need to
declare a script to call Eternaltwin: yarn eternaltwin already works. You may
still add an entry such as "eternaltwin": "eternaltwin" to the scripts
section if you prefer an explicit alias, or if you use npm.
⚠ The package was previously named @eternal-twin/website or @eternal-twin/cli, it was renamed to @eternaltwin/cli.
Make sure you use the right package.
Start Eternaltwin
Once Eternaltwin is installed, you can run it from anywhere inside your repo using the following command:
yarn eternaltwin start
This command starts the local Eternaltwin server on your computer. You can use this server to test your project.
No configuration file is required: without one, Eternaltwin uses the built-in
dev profile, which keeps all the data in memory and is lost on restart.
When starting, the server displays where each configuration source came from
and the resolved configuration (with backend.secret blanked out), then one
line per seeded user and per registered OAuth client. You can use this
information to troubleshoot your configuration.
The built-in dev profile already seeds a handful of test users (alice /
aaaaaaaaaa, bob / bbbbbbbbbb, and so on), a few forum sections and the
OAuth clients of the historical Eternaltwin games, so you have something to log
in with before configuring anything.
By default, the server uses the port 50320 and is available at the address
http://localhost:50320/. start serves the website and the REST API on that
single port, so http://localhost:50320/ is also the origin your application
uses for the OAuth endpoints. (Inside the Eternaltwin repository itself, the
website is served separately by the Angular dev server on port 50321; that
split does not apply to the packaged version.)
Configure Eternaltwin
You can configure Eternaltwin to your personal preferences (the most notable is being able to run it with a Postgres database so you can have a persistent environment).
Eternaltwin looks for its configuration in the current directory and then in
its parents. For the default dev profile, the file names are tried in the
following order, and the first match wins:
eternaltwin.dev.local.tomleternaltwin.dev.local.jsoneternaltwin.dev.tomleternaltwin.dev.jsoneternaltwin.local.tomleternaltwin.local.jsoneternaltwin.tomleternaltwin.json
(with another profile, dev in the first four names is replaced by the profile
name; the sdk profile is special and reads no configuration file at all).
The *.local.* files are meant to hold values specific to your machine and
should not be stored in Git. The recommended strategy is therefore:
- Create a file named
eternaltwin.tomlat the root of your repository, with the settings shared by all the contributors, and commit it. You may start from Eternaltwin's own commented configuration (raw), which documents every available key. - Add the
eternaltwin.local.tomlrule to your.gitignore. - Update your project setup documentation: contributors who need
machine-specific settings (a Postgres connection, real mail credentials, ...)
copy
eternaltwin.tomltoeternaltwin.local.tomland edit that copy.
The available keys and the full resolution algorithm are described in
Eternaltwin server configuration. The section you will
need first is [seed.app]: it is where you register your own game as an OAuth
client, as described in Eternaltwin for OAuth.
Run yarn eternaltwin config to load the configuration, print the resolved
result and exit, without starting a server. This is the quickest way to check
which files were picked up and which values won.
Other commands
yarn eternaltwin forwards its arguments to the Eternaltwin executable, which
provides the following subcommands:
yarn eternaltwin start: start the full website (REST API plus the web frontend)yarn eternaltwin backend: start the backend only (REST API and action handlers, no frontend). Accepts--format json|jsonl|textto emit machine-readable server eventsyarn eternaltwin config: resolve and print the configuration, then exityarn eternaltwin db check: check the state of the Postgres database used by the dev website if configured to use thePostgresstoreyarn eternaltwin db reset: initialize an empty databaseyarn eternaltwin db sync: upgrade an existing database to the latest schema versionyarn eternaltwin dump: dump the database state into a directoryyarn eternaltwin job: control the background jobsyarn eternaltwin version: print the version of the executable
All the commands that need the configuration accept the same options, most
notably --profile <name> (default: dev) and --config <uri>.
PHP projects
If your project is a PHP project, the equivalent of @eternaltwin/cli is the
Composer package eternaltwin/cli. It requires PHP >=8.1.
composer require --dev eternaltwin/cli
The executable itself is provided by the eternaltwin/exe package, which
downloads the precompiled binary at install time (or builds it with cargo if
your platform has no precompiled build). It does so through a Composer compile
plugin, so your own composer.json must allow that plugin and whitelist the
package:
{
"config": {
"allow-plugins": {
"civicrm/composer-compile-plugin": true
}
},
"extra": {
"compile-whitelist": [
"eternaltwin/exe"
]
}
}
Eternaltwin is then available as vendor/bin/eternaltwin, with the same
subcommands as above:
./vendor/bin/eternaltwin start
The PHP client for the Eternaltwin API is a separate package,
eternaltwin/etwin; see Eternaltwin API.
Running Eternaltwin from your tests
Beyond the command line, a local Eternaltwin server can be started and stopped programmatically, so your test suite can run against a real server instead of a mock.
For Node, this is what @eternaltwin/sdk does. Install it together with the
executable and the Node API client:
yarn add --dev @eternaltwin/sdk @eternaltwin/exe @eternaltwin/client-node
import {EternaltwinNodeClient} from "@eternaltwin/client-node";
import {getExecutableUri} from "@eternaltwin/exe";
import {Sdk} from "@eternaltwin/sdk";
const exe = await getExecutableUri();
const sdk = await Sdk.fromExe(exe);
const server = await sdk.startServer({profile: "sdk"});
try {
const uri = server.getUri();
if (uri === null) {
throw new Error("failed to retrieve the server URI");
}
// `uri` is the root of the server: the client appends `api/v1` itself.
const client = new EternaltwinNodeClient(uri);
const auth = await client.getAuthSelf({auth: "invalidtoken"});
console.log(auth);
} finally {
await server.stop();
}
startServer spawns eternaltwin backend --format jsonl and resolves once the
server reports that it is listening, so there is no need to poll the port.
EternaltwinServer also implements Symbol.asyncDispose, so if your sources go
through TypeScript (5.2 or later) you may write
await using server = await sdk.startServer(...) and drop the try/finally,
as Eternaltwin's own tests do.
The profile option selects the configuration profile:
sdk(the default) reads no configuration file at all and always uses the same built-in settings: everything in memory, a virtual clock and mocked outbound clients. This is the profile to use for reproducible tests.testuses the built-in test profile, which expects Postgres, and does readeternaltwin.test.toml/eternaltwin.test.local.tomlfrom the working directory, so you can override the store withstore = "Memory"as Eternaltwin's own test configuration does.
Because the sdk profile reads no file, it cannot be given your own OAuth
client: it only seeds the same built-in test users and historical game clients
as dev. Tests that go through the OAuth flow therefore use the test profile
together with an eternaltwin.test.toml declaring the application under
[seed.app] — this is exactly what Eternaltwin's own SDK tests do.
ℹ The PHP equivalent, eternaltwin/test-server, is still a work in
progress and should not be relied on yet. For PHP tests, start
vendor/bin/eternaltwin backend --profile sdk yourself and stop it when the
suite ends.
Next steps
Now that your repo is configured to run Eternaltwin, you may start to actually integrate your project with Eternaltwin. The first step would be to use Eternaltwin to manage user accounts through OAuth.