Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Pythia

[![Build](https://github.com/jonjau/pythia/actions/workflows/rust.yml/badge.svg)](https://github.com/jonjau/pythia/actions/workflows/rust.yml)
[![License:MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Pythia is a novel 'state change explorer' tool built with Rust and [Scryer Prolog](https://www.scryer.pl/).

Run unit tests with database changes tracked in Pythia to help answer questions like:
- How many unique database records of a certain table does Test `T1` change?
- Which tests cover the mutation of field `F`, for example from 'ordered', 'dispatched', then 'delivered'?
- Is state `S` is reachable from state `S'` in the context of Test `T1` or Test `T2`?
- If field `F` changes, do any other fields tend to change with it?
- How many database modifications would it take, to take a database record from state `S` to state `S'`?
- If we were to run the functionality covered by Test `T1` followed by that of Test `T2` on the same database record, what would be the resulting state?

Run completely locally with `docker` or `podman`, or try out the public demo at [pythia.jonjauhari.com](https://pythia.jonjauhari.com) (I don't always keep it running).

## Built with

- Rust and Axum: server for HTML and exposing a REST API, with cookie-based anonymous sessions.
- Scryer Prolog: underlying logic engine for graph reachability calculations.
- Askama: Prolog code generation and HTML templating.
- HTMX, Alpine.js, and TailwindCSS: lightweight web-based UI with barely any JavaScript.
- DynamoDB: single-table database to persist all application data.
- AWS (Fargate), Docker, Terraform, GitHub Actions: continuous deployment workflow.
- Cloudflare: DNS provider.

## How to run

### Run locally with Docker/Podman

At the project root:

```bash
docker compose up
```

Pythia will be running locally and listening on port `3000`. All data will be locally stored in a `.db` file under `dynamodb/`.

### Run locally without Docker/Podman

Linux/Windows binaries for Pythia are available in 'Releases' in this repo.

Some useful environment variables that can be set:

- `PYTHIA_RUN_MODE`: `local` (default if unspecified) or `remote`.
- `AWS_ENDPOINT_URL`: the DynamoDB endpoint to use for persistence. This is required for the `local` run mode, and ignored for `remote` run mode. The regional DynamoDB endpoint is used for the `remote` run mode.
- `RUST_LOG`: the log level; `INFO` is good.

For example you can run the Pythia executable by pointing to a separately running DynamoDB instance on port `8000`:

```bash
AWS_ENDPOINT_URL="http://localhost:8000" RUST_LOG=info ./path/to/executable
```

Or build it directly if you have Cargo installed:

```bash
cargo build && AWS_ENDPOINT_URL="http://localhost:8000" RUST_LOG=info ./target/debug/pythia
```

### Run on AWS

Ensure you have authenticated with the AWS CLI with enough permissions, a Cloudflare API token, and have Terraform installed.

In `infra/bootstrap/`, run `terraform init` and `terraform apply`. This project will setup an S3 bucket that can be used as a backend for the other Terraform projects (and also an IAM role for the GitHub Actions deployment automation).

In `infra/ecr/`, init terraform with a backend (ideally a remote location like S3) then `terraform apply`. This project will set up the ECR repository.

In `infra/`, init terraform with a backend (ideally a remote location as well) then `terraform apply`. This project will set up the actual Pythia application deployment, including:

- ECS: cluster, Fargate service, task definition
- IAM roles: for ECS and the ECS Task
- Networking: AWS Subnets, gateways, route tables, the ALB. The EC2-based [`fck-nat`](https://registry.terraform.io/modules/RaJiska/fck-nat/aws/latest) module is used in place of AWS's managed NAT gateway in order to cut down on running costs.
- DNS: ACM certificate, Cloudflare DNS records

## Usage

1. Start a session:

<img src="doc/sessions.png" width="500" />

2. Add record types (either via web UI or the REST API):

<img src="doc/record-types.png" width="500" />

3. Add facts for the records (either via the web UI or the REST API):

<img src="doc/facts.png" width="500" />

4. Calculate state change paths!

<img src="doc/state-change-1.png" width="500" />
<img src="doc/state-change-2.png" width="500" />

There's a few things we can loosely infer:
- `Test_FailOrder` tests 2 unique 'order' records in total.
- Both tests cover the mutation of `Status` from 'ordered' to 'dispatched'.
- Starting from the `Status` of 'ordered' we can reach the `Status` of 'cancelled' or 'delivered'.
- When the `Status` changes to 'dispatched', the `DispatchDate` is set to some date, likewise for 'delivered' and `DeliveryDate`.
- It takes 2 steps to get from `Status` of 'ordered' to 'delivered'.
- We could probably cancel 'Ord2' before it goes to the 'delivered' `Status`.

## License

Pythia is currently licensed under the terms of both the MIT license and the Apache License (Version 2.0). See [`LICENSE-MIT`](/LICENSE-MIT) and [`LICENSE-APACHE`](/LICENSE-APACHE) for more details.

13 changes: 0 additions & 13 deletions data/dimlink.pl

This file was deleted.

52 changes: 0 additions & 52 deletions data/internal/pythia.pl

This file was deleted.

12 changes: 0 additions & 12 deletions data/transaction.pl

This file was deleted.

39 changes: 0 additions & 39 deletions data/types.json

This file was deleted.

Binary file added doc/facts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/record-types.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/sessions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/state-change-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/state-change-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion templates/fact/facts-table.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<td class="p-3 border-b border-stone-700">
<span x-data="{ copied: false }"
@click="navigator.clipboard.writeText('{{ value }}'); copied = true; setTimeout(() => copied = false, 1500)"
class="cursor-pointer font-mono text-amber-200 hover:text-amber-400">
class="cursor-pointer text-amber-200 hover:text-amber-400">
<span x-show="!copied">{{ value }}</span>
<span x-show="copied" class="text-teal-400">Copied!</span>
</span>
Expand Down
12 changes: 6 additions & 6 deletions templates/how-to-use.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ <h2 class="text-4xl font-bold mb-7 pt-3">What is this?</h2>

<p class="m-0 mb-5">
<span class="font-bold text-amber-200">Pythia</span> is an open-source '<span class="italic">state change
explorer</span>'. It's a tool to track how
explorer</span>'. It's a tool to visualise and search how
<span class="font-bold">records</span> change over time, based on recorded <span class="font-bold">facts</span>
for those <span class="font-bold">record types</span>.
</p>
Expand All @@ -16,7 +16,7 @@ <h2 class="text-4xl font-bold mb-7 pt-3">What is this?</h2>
</p>

<p class="m-0 mb-5">
<span class="font-bold text-amber-200">Pythia</span>'s intended use case is to aid in reverse-engineering the
<span class="font-bold text-amber-200">Pythia</span>'s intended use case is to aid in inferring the
rules that produced the facts which are recorded.
</p>

Expand All @@ -43,14 +43,14 @@ <h2 class="text-4xl font-bold mb-7 pt-3">How do I use it?</h2>
<p class="m-0 mb-5">
<ol class="list-decimal list-outside space-y-2">
<li>Get a user token, via <a href="/sessions" class="link">'Manage session'</a> which will set a <span
class="font-mono text-amber-200">user_token</span>
class="text-amber-200">user_token</span>
as a cookie. <br> This token is displayed at the top right of the page.</li>
<li>Define record types via <a href="/record-types" class="link">'Record types'</a>, or POST to the API route:
<br> <span class="font-mono text-amber-200">/api/record-types</span> (the
<span class="font-mono text-amber-200">user_token</span> cookie must be set)
<br> <span class="text-amber-200">/api/record-types</span> (the
<span class="text-amber-200">user_token</span> cookie must be set)
</li>
<li>Add facts for record types under <a href="/inquiries" class="link">'Inquiries'</a>, or POST to the API
route: <br> <span class="font-mono text-amber-200">/api/{record_type}/facts</span>.</li>
route: <br> <span class="text-amber-200">/api/{record_type}/facts</span>.</li>
<li>Calculate state change paths under <a href="/inquiries" class="link">'Inquiries'</a> based on the recorded
facts.</li>
<li>Narrow it down with filters, and make conclusions about the data.</li>
Expand Down
2 changes: 1 addition & 1 deletion templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
{% endif %}
<span x-data="{ copied: false }"
@click="navigator.clipboard.writeText('{{ user_token }}'); copied = true; setTimeout(() => copied = false, 1500)"
class="cursor-pointer font-mono text-amber-200 hover:text-amber-400">
class="cursor-pointer font-monospace text-amber-200 hover:text-amber-400">
<span x-show="!copied">{{ user_token }}</span>
<span x-show="copied" class="text-teal-400">Copied to clipboard!</span>
</span>
Expand Down
9 changes: 0 additions & 9 deletions terraform.tfstate

This file was deleted.

Loading