Sequent Voting Platform is an end-to-end verifiable, secure, and transparent online voting system. This monorepo contains the second generation of the platform, designed for real-world elections with strong cryptographic guarantees.
- End-to-end verifiability: Voters can verify their vote was recorded, tallied, and counted correctly
- Privacy-preserving: Cryptographic mixnet ensures ballot secrecy while maintaining verifiability
- Multi-tenant: Support for multiple organizations and simultaneous elections
- Accessible: Web-based interfaces optimized for accessibility and usability
- Auditable: Comprehensive tamper-evident logging of all system operations
- Hasura - GraphQL API layer
- Rust - Cryptographic core and backend services
- Keycloak - Identity and access management
- PostgreSQL - Data persistence
- React - Frontend user interfaces
- ImmuDB - Tamper-evident audit logging
For comprehensive documentation, visit docs.sequentech.io.
The fastest way to start developing is using VS Code Dev Containers or GitHub Codespaces:
- Clone this repository
- Open in VS Code with Dev Containers extension
- All services will start automatically
The development environment includes:
- Keycloak at http://127.0.0.1:8090
- Hasura Console at http://127.0.0.1:8080
- Voting Portal at http://127.0.0.1:3000
- Admin Portal at http://127.0.0.1:3002
- ImmuDB Console at http://127.0.0.1:3325
- MinIO at http://127.0.0.1:9001
For detailed setup instructions, see the Documentation.
If you prefer not to use Dev Containers:
- Install prerequisites: Rust, Node.js/Yarn, Docker, PostgreSQL
- Set up environment variables (see
.env.example) - Start services:
docker compose up -d - Run migrations:
cd hasura && hasura migrate apply - Start frontend:
cd packages && yarn && yarn dev
See the Documentation for detailed instructions.
.
├── .devcontainer/ # Dev container configuration
├── docs/docusaurus/ # Documentation site
├── hasura/ # GraphQL schema, migrations, and metadata
├── packages/ # Monorepo packages (Cargo + Yarn workspace)
│ ├── admin-portal/ # Admin interface (React)
│ ├── voting-portal/ # Voter interface (React)
│ ├── braid/ # Cryptographic mixnet (Rust)
│ ├── sequent-core/ # Shared core libraries (Rust)
│ ├── ui-essentials/ # Shared UI components
│ └── ... # Other packages
└── README.md
The packages/ directory is both a Cargo workspace and a Yarn workspace, enabling code sharing between frontend (compiled to WebAssembly) and backend (native Rust).
We welcome contributions! Please see our Contributing Guide for details.
This project is licensed under the AGPL-3.0-only license. See LICENSE for details.
- Documentation: https://docs.sequentech.io/docusaurus/main/
- Discord: Join our Discord community
- Issues: Report bugs on GitHub Issues
For detailed developer documentation including advanced setup, architecture details, and troubleshooting, see the sections below or visit the complete documentation.
We have configured the use of direnv and devenv in this dev container, and
doing so in the devenv.nix file we configured the
COMPOSE_PROJECT_NAME=step_devcontainer env variable for
convenience and some utility packages automatically installed like ack or
docker.
Given that, you can then for example watch the log output of the frontend docker compose service with:
docker compose logs -f frontend
And do the same for the other services. You could also do anything
docker-compose allows for example list running commands withdocker compose ps.
With regards to the logs, we have configured in .vscode/tasks.json to
automatically run docker compose logs on start up, for convenience.
You can enter the Immudb web console at http://localhost:3325 and the user/pass is immudb:immudb.
The deployment has 2 default Keycloak realms created by default, one for the default tenant and another for the default election event inside that tenant.
Those two realms are automatically imported into Keycloak in the Dev Containers
from the .devcontainer/keycloak/import/ directory.
Additionally, each tenant and election event have an associated realm. In the
Dev Containers, we use the same .devcontainer/keycloak/import/ files to be the
templates for the creation of realms associated to a new tenant or a new
election event. These realms are created if they don't exist when the keycloak
container is started.
If you change the configuration of the default tenant realm and want to update
it in .devcontainer/keycloak/import/ to be used for the default tenant and as
a template for new tenants, you can export it running the following commands:
export REALM="tenant-90505c8a-23a9-4cdf-a26b-4e19f6a097d5"
cd /workspaces/step/.devcontainer
docker compose exec keycloak sh -c "/opt/keycloak/bin/kc.sh export --file /tmp/export.json --users same_file --realm ${REALM}"
docker compose exec keycloak sh -c 'cat /tmp/export.json' > keycloak/import/${REALM}.jsonYou can change REALM to be "tenant-90505c8a-23a9-4cdf-a26b-4e19f6a097d5-event-33f18502-a67c-4853-8333-a58630663559" to export and update the configuration of the default election event:
export REALM="tenant-90505c8a-23a9-4cdf-a26b-4e19f6a097d5-event-cd1397d3-d236-42b4-a019-49143b616e13"
cd /workspaces/step/.devcontainer
docker compose exec keycloak sh -c "/opt/keycloak/bin/kc.sh export --file /tmp/export.json --users same_file --realm ${REALM}"
docker compose exec keycloak sh -c 'cat /tmp/export.json' > keycloak/import/${REALM}.jsonWhenever a realm is updated, there's a chance that the associated JWK used have
changed. This JWK is used to verify the JWT that is received from keycloak.
These keys are configured in S3/minio in the .devcontainer/minio/certs.json
file via the logs.reset.minio helper docker service. If the keys changed and we
don't update the keys serviced by minio/s3, then the admin-portal or the
voting-booth might show some errors because this JWT verification fails.
To fix this issue, run the VS Code task logs.reset.minio which will:
- Fetch JWK certificates from Keycloak realms configured in
.devcontainer/keycloak/import/ - Update the
.devcontainer/minio/certs.jsonfile with the combined certificates - Rebuild and restart the MinIO configuration service
- Display the logs for verification
NOTE: that this is a task that is useful only for developers: when you
execute it, the certificates from other election events will be removed from
certs.json so you will need to re-create the election events.
If you want to make changes to hasura, or if you want the Hasura console to
automatically add migrations to the code, first run this project in Codespaces
and open it in VS Code Desktop (not from the web). Then, in your local machine
ensure that the graphql-engine server name is aliased to 127.0.0.1 in
/etc/hosts, or else this won't work.
Then run the following commands to run the console in port 9695:
cd /workspaces/step/hasura/
hasura console --endpoint "http://graphql-engine:8080" --admin-secret "admin"Then open http://localhost:9695 on the browser and make the changes you need.
Those changes will be tracked with file changes on the Github Codespaces, then
commit the changes.
Note that you can insert rows as a migration by clicking on the
This is a migration option at the bottom of the Insert Row form.
Note: if the browser doesn't load correctly at http://localhost:9695, try
opening the port 9693 in VS Code.
Contains all the components used across the various portals i.e admin, voting, ballot etc. Has storybook configured for component documentation and easy update of existing components or building new ones
To start storybook,
cd /workspaces/step/packages/
yarn storybook:ui-essentialsAfter updating any component in ui-essentials, run the following commands to build the current state.
cd /workspaces/step/packages/
yarn prettify:fix:ui-essentials && yarn build:ui-essentialsThis is done to allow portals to fetch and use the latest versions of components
The file packages/admin-portal/graphql.schema.json contains the GraphQL/Hasura
schema. If the schema changes you might need to update this file by running the VS Code task update.graphql.
It might be the case that for example if you added some new field to an existing
table, you will have to update some graphql query in
packages/windmill/src/graphql/ directory and the corresponding boilerplate
code in packages/windmill/src/hasura/. Otherwise the build might fail.
By default the trustees in this repo are configured to use a predefined configuration/
set of keys. This is useful for development because these trustees are also added to
the hasura/postgres database. This configuration is set using the TRUSTEE_CONFIG
environment paramenter in the docker-compose.yml file.
However if you want the trustees to generate their own unique public/private keys and configuration this is is what you need to do:
First unset the TRUSTEE_CONFIG environment variable or set it to a file path that
doesn't exist. Then, when the trustee docker container is up, get the keys from the trustee:
docker exec -it trustee1 cat /opt/braid/trustee.toml | grep pkWhich will give a result similar to:
signing_key_pk = "YqYrRVXmPhBsWwwCgsOfw15RwUqZP9EhwmxuHKU5E8k"Then add the trustee in the admin portal with the key, in this case YqYrRVXmPhBsWwwCgsOfw15RwUqZP9EhwmxuHKU5E8k.
To run the windmill task generator (beat), use the VS Code task logs.restart.beat.
To run the trustees, use the VS Code task logs.restart.trustees.
## Common issues
You run out of memory. Run in a bigger codespace or stop some service before
proceeding. You can stop for example the frontend with
docker compose stop frontend
I changed my Dockerfile or my docker-compose.yml. Can I relaunch it without rebuilding the whole Dev Container?
Yes you can. For example, if you need to apply a new DockerFile o
docker-compose.yml config just for the frontend service, you can do the
following:
# First rebuild the service from the Dockerfile as indicated in the
# docker-compose.yml, then relaunch just the frontend service
docker compose build frontend && docker compose up -d --no-deps frontendAdd the query/mutation to the packages/voting-portal/src/queries/ folder and
then run yarn generate from the packages/ folder to update the types.
Similarly, run yarn generate:admin-portal to update the types of the
admin-portal if you need it.
It's possible you find that the voting portal is not loading any elections, and
that inspecting it further, the Hasura/Graphql POST gives an error similar to
field not found in type: 'query_root'. This is possibly because you're
connecting to the wrong instance of Hasura. Possibly, you're running VS Code
with Codespaces and a local Hasura client as well, so the container port is
being forwarded to a different port than 8080.
This is a nasty error that we need to further investigate and fix. Typically start happening after the codespace has been stopped and restarted a few times. Currently the only fix we have is.. committing and pushing all your changes to your branch and starting a new codespace.
Clean the disk with:
docker system prune --all --force
nix-collect-garbage
cargo cleanIf you see an error when starting/restarting a container, remove the .docker folder:
rm -rf /home/vscode/.docker/Unfortunately commiting with git doesn't work from the devcontainer. To commit to git, ssh into the instance and cd into the step folder, then commit using git.
If you're getting a permission error when building sequent-core, do:
sudo mkdir /workspaces/step/packages/target
sudo chown vscode:vscode /workspaces/step/packages/target -RIf you're getting an error of this kind
vscode ➜ /workspaces/step (main) $ devenv shell
• Building shell ...
• Using Cachix: devenv
thread 'main' panicked at /tmp/nix-build-devenv-1.4.0.drv-0/source/devenv/src/cnix.rs:157:40:
to resolve gc_root: Os { code: 2, kind: NotFound, message: "No such file or directory" }
...when running in the terminal devenv shell or in the Rebuild Container vscode task. This can happen when devenv version was updated but the cache wasn´t, try:
- Remove folders
step/.direnvandstep/.devenv. - Then run
Rebuild Container Without Cache.