• Stars
    star
    957
  • Rank 47,767 (Top 1.0 %)
  • Language
    Haskell
  • License
    BSD 3-Clause "New...
  • Created over 6 years ago
  • Updated 28 days ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Functional reactive web and mobile applications, with batteries included.

Obelisk

Haskell Programming Language BSD3 License

Obelisk Logo

Functional reactive web and mobile applications, with batteries included. Obelisk's goal is to represent a cohesive, highly-curated set of choices that Obsidian Systems has made for building these types of applications in a way that is extremely fast but does not compromise on production readiness.

Overview

Obelisk allows you to build high-quality web and mobile applications very quickly using Reflex. In minutes you can go from an empty directory to an interactive application that works on web, iOS, and Android, all sharing the same Haskell codebase! Obelisk's development environment also enables extremely rapid development and feedback. You can take advantage of Haskell's type system across the frontend and backend boundary. This means changes to your backend that would break your frontend are immediately detected during development and vice versa. Obelisk uses Haskell's compiler to give you a complete "TODO list" of what needs to be updated.

Obelisk is targeted primarily at Haskell developers who want to build high-quality web and/or mobile applications in Haskell, without the distractions of manually choosing and integrating technology for every piece of the system.

Who should consider using it?

Obelisk assumes basic knowledge of Haskell and Reflex/Reflex-DOM, web technologies like HTML and CSS, and a terminal shell like Bash. Knowledge of Nix helps but is not strictly necessary.

Installing Obelisk

  1. Install Nix. If you already have Nix installed, make sure you have version 2.0 or higher. To check your current version, run nix-env --version.
  2. Set up nix caches
    1. If you are running NixOS, add this to /etc/nixos/configuration.nix:
      nix.binaryCaches = [ "https://nixcache.reflex-frp.org" ];
      nix.binaryCachePublicKeys = [ "ryantrinkle.com-1:JJiAKaRv9mWgpVAz8dwewnZe0AzzEAzPkagE9SP5NWI=" ];
      and rebuild your NixOS configuration (e.g. sudo nixos-rebuild switch).
    2. If you are using another operating system or Linux distribution, ensure that these lines are present in your Nix configuration file (/etc/nix/nix.conf on most systems; see full list):
      binary-caches = https://cache.nixos.org https://nixcache.reflex-frp.org
      binary-cache-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= ryantrinkle.com-1:JJiAKaRv9mWgpVAz8dwewnZe0AzzEAzPkagE9SP5NWI=
      binary-caches-parallel-connections = 40
      • If you're on a Linux distribution other than NixOS, enable sandboxing (see these issue 172 or issue 6 if you run into build problems) by adding the following:
        sandbox = true
        then restart the nix daemon
        sudo systemctl restart nix-daemon
      • If you're on MacOS, disable sandboxing (there are still some impure dependencies for now) by adding the following:
        sandbox = false
        then restart the nix daemon
        sudo launchctl stop org.nixos.nix-daemon
        sudo launchctl start org.nixos.nix-daemon
  3. Install obelisk:
    nix-env -f https://github.com/obsidiansystems/obelisk/archive/master.tar.gz -iA command

Accessing private repositories

To allow the Nix builder to access private git repositories, you must be set up to access them via SSH. Follow these steps depending on the platform you need access to:

Developing an Obelisk project

To create a new Obelisk project, go to an empty directory and run:

ob init

Obelisk leverages ghcid to provide a live-reloading server that handles both frontend and backend. To run your Obelisk app and monitor the source for changes:

ob run

Now, with an appropriate browser, go to http://localhost:8000 (or the address/port specified in config/common/route) to access your app. Firefox will not be able to properly run the development website due to issue 460. Fortunately, this problem does not occur on a fully built website.

Every time you change the Haskell source files in frontend, common or backend, ob run will automatically recompile the modified files and reload the server. Furthermore, it will display on screen compilation errors and warnings if any.

Local Hoogle

Obelisk can also provide a local Hoogle server that lets you browse and search the types and documentation for all of the dependencies in your entire Obelisk application. To start the Hoogle server, in a spare terminal run the following command from the root of your Obelisk application:

$ ob hoogle

You can then access your local Hoogle from your web browser at http://localhost:8080, or by instructing an editor plugin to use that address.

Adding packages

In order to add package dependencies, declare them under the build-depends field in the appropriate cabal files (backend, common, and frontend each have their own). The corresponding Nix packages will automatically be selected when building.

Adding package overrides

To add a version override to any Haskell package, or to add a Haskell package that doesn't exist in the nixpkgs used by Obelisk, use the overrides attribute in your project's default.nix. For example, to use a specific version of the aeson package fetched from GitHub and a specific version of the waargonaut package fetched from Hackage, your default.nix will look like:

# ...
project ./. ({ pkgs, ... }: {
# ...
  overrides = self: super: let
    aesonSrc = pkgs.fetchFromGitHub {
      owner = "obsidiansystems";
      repo = "aeson-gadt-th";
      rev = "ed573c2cccf54d72aa6279026752a3fecf9c1383";
      sha256 = "08q6rnz7w9pn76jkrafig6f50yd0f77z48rk2z5iyyl2jbhcbhx3";
    };
  in
  {
    aeson = self.callCabal2nix "aeson" aesonSrc {};
    waargonaut = self.callHackageDirect {
      pkg = "waargonaut";
      ver = "0.8.0.1";
      sha256 = "1zv28np3k3hg378vqm89v802xr0g8cwk7gy3mr77xrzy5jbgpa39";
    } {};
  };
# ...

For further information see the Haskell section of nixpkgs Contributors Guide.

Adding extra local packages

If the standard packages (frontend, backend, and common) are not enough, to add more local Haskell packages, define them with the packages parameter. The sources of these packages will be automatically reloaded by ob run.

# ...
project ./. ({ pkgs, ... }: {
# ...
  packages = {
    another = ./another;
  };
# ...

Running over HTTPS

To run your app locally over HTTPS, update the protocol in config/common/route to https, and then use ob run as normal.

Since Obelisk generates a self-signed certificate for running HTTPS, the browser will issue a warning about using an invalid certificate. On Chrome, you can go to chrome://flags/#allow-insecure-localhost to enable invalid certificates for localhost.

IDE Support

Obelisk officially supports terminal-based feedback (akin to ghcid) in ob run and ob watch.

Using GHC 8.10

Obelisk currently uses GHC 8.6 for projects by default, since this is the version on which Obelisk (and reflex-platform more generally) have been most thoroughly tested. However, we understand that this version is significantly behind GHC releases, and thus have experimental support for building with GHC 8.10 instead. To build with GHC 8.10, add the following to your project's default.nix:

  { system ? builtins.currentSystem
  , obelisk ? import ./.obelisk/impl {
      inherit system;
+     useGHC810 = true;

If the useGHC810 argument is set to false, or not given, then GHC 8.6 will be used.

Deploying

Default EC2 Deployment

In this section we will demonstrate how to deploy your Obelisk app to an Amazon EC2 instance. Obelisk deployments are configured for EC2 by default (see Custom Non-EC2 Deployment).

Note: Most NixOS EC2 instances should just work regardless of obelisk version

First create a new EC2 instance:

  1. Launch a NixOS 22.05 EC2 instance (we recommend this AMI)
  2. In the instance configuration wizard ensure that your instance has at least 1GB RAM and 10GB disk space.
  3. When prompted save your AWS private key (~/myaws.pem) somewhere safe. We'll need it later during deployment.
  4. Go to "Security Groups", select your instance's security group and under "Inbound" tab add a new rule for HTTP port 80 and HTTPS port 443.

At this stage your instance should be booting and become accessible shortly. Note down the hostname of your EC2 instance.

Now go to your Obelisk project directory (~/code/myapp), and initialize a deployment config (~/code/myapp-deploy): Your project directory must be "thunkable", i.e. something on which ob thunk pack can be called. Usually it will be a git repository whose current revision has been pushed upstream.

An example set of git commands to do this is as follows (Github): Create a repo using Github's UI (Public or Private) then locally use these commands

cd ~/code/myapp
git init
git add .
git commit -m "First Commit!"
git remote add origin [email protected]:username/repo.git
git push --set-upstream origin master

This will make a "thunkable" project that allows deployment to continue

Continuing with deployment commands:

cd ~/code/myapp
SERVER=ec2-35-183-22-197.ca-central-1.compute.amazonaws.com
ROUTE=https://myapp.com   # Publicly accessible route to your app
[email protected]
ob deploy init \
  --ssh-key ~/myaws.pem \
  --hostname $SERVER \
  --route $ROUTE \
  --admin-email $EMAIL \
  ~/code/myapp-deploy

HTTPS is enabled by default; to disable HTTPS pass --disable-https to the ob deploy init command above.

This step will also require that you manually verify the authenticity of the host $SERVER. You can specify that you want ob deploy init to check your ~/.ssh/known_hosts file and save any fingerprints matching the host to the deployment-specific configuration by passing the --check-known-hosts option to the deploy init command. Note that --check-known-hosts only works when there is a single keypair associated with a given host.

REMARK (Security): Obelisk deployments do not rely on the known_hosts of your local machine during deployment, only potentially during the ob deploy init, as previously mentioned. This is because, in the event that you need to switch from one deploy machine / bastion host to another, you want to be absolutely sure that you're still connecting to the machines you think you are, even if that deploy machine / bastion host has never connected to them before. Obelisk explicitly avoids a workflow that encourages people to accept host keys without checking them, since that could result in leaking production secrets to anyone who manages to MITM you, e.g. via DNS spoofing or cache poisoning. Note that an active attack is a circumstance where you may need to quickly switch bastion hosts, e.g. because the attacker has taken one down or you have taken it down in case it was compromised. In this circumstance you might need to deploy to production to fix an exploit or rotate keys, etc. When you run ob deploy later it will rely on the saved verification in this step.

Next, go to the deployment directory that you just initialized and deploy!

cd ~/code/myapp-deploy
ob deploy push

ob deploy push will locally build your app and then transfer it, along with all the Nix package dependencies, via ssh to the EC2 instance. The backend will live in /var/lib/backend.

At this point you are done. Your app will be accessible at ${ROUTE}. The currently deployed version - the git commit hash of the source repo - can be found at ${ROUTE}/version.

Custom Non-EC2 Deployment

By default Obelisk deployments are configured for NixOS machines running on AWS EC2. To provide your own configuration, you need to write a custom module.nix in the deployment repository. This still requires that your server is running NixOS.

module.nix must contain a Nix function that produces a NixOS module function. The top-level function takes deployment configuration as arguments: hostName, adminEmail, routeHost, enableHttps, version, exe, nixosPkgs. Most of these are the values you specified during ob deploy init and are stored in the deployment repository. version is a git hash for the app that you're deploying. exe is the Linux build of the app (as seen in Deploying Locally). nixosPkgs is the package set used to construct the NixOS VM.

The VirtualBox Deployment section provides an example.

VirtualBox Deployment

Here's a module.nix that is configured for deployment to a VirtualBox VM (running NixOS):

{ nixosPkgs, ... }: {...}: {
  imports = [ (nixosPkgs.path + /nixos/modules/virtualisation/virtualbox-image.nix) ];
}

The {...}: and following is the NixOS module definition.

Locally

If you want deploy your application locally or test a production-oriented build you can build and deploy the app as described below.

Build the application:

nix-build -A exe --no-out-link

Copy the result to a new directory, add configuration, and run!

mkdir test-app
ln -s $(nix-build -A exe --no-out-link)/* test-app/
cp -r config test-app
(cd test-app && ./backend)

From macOS

Deploying from macOS requires some extra setup:

Running ob deploy push will give you additional setup instructions.

Deploying an updated version

If you'd like to deploy an updated version (with new commits) of your Obelisk app: simply go to the configuration directory, update the source thunk and push:

cd ~/code/myapp-deploy
ob deploy update
ob deploy push

Host Redirection

A redirect_hosts file can be added in the deployment directory (~/code/myapp-deploy in the example above), allowing you to specify alternative domain names that will redirect to the deployment domain. This feature assumes the apropriate CNAME records have been added with a domain registration service.

Add one domain per line in redirect_hosts. All listed domains will redirect to the publicly accessible domain specified by ob deploy init. For clarity, this is the $ROUTE variable in the EC2 deployment example shown earlier. The following is an example of a ~/code/myapp-deploy/redirect_hosts file:

www.foo.com
www.bar.com

Caveat: Your https certificates will cover all your domains automatically, although you may need to force a recertification manually. We assume you have root access to the deployment EC2 instance. Continuing from the ob init deploy example above:

ssh [email protected]

[email protected]
ROUTE_TO=myapp.com
ROUTE_FROM=foo.com
ROUTE_FROM_2=bar.com
/nix/store/`ls /nix/store | grep lego`/bin/lego \
  -d $ROUTE_TO \
  --email $EMAIL \
  --path . \
  --key-type ec256 \
  --accept-tos \
  -d $ROUTE_FROM \
  -d $ROUTE_FROM_2 \
  --http \
  --http.webroot /var/lib/acme/acme-challenge run

Mobile

Until Obelisk offers a ob deploy equivalent for mobile apps, you are recommended to do it manually as follows.

iOS

First time setup

Development on iOS requires a computer running macOS and an iOS developer account. Your account must also belong to an Apple Developer Team, if you want to access developer portal links (otherwise they'll redirect to your account page).

iPhone
  1. Connect the iPhone on which you'd like to run builds - this will open up iTunes.
  2. Click accept to authorize on both the computer and the iPhone.
Xcode

Install Xcode 11.2 (contains iOS SDK 13.2) and open it so that it runs its post install tool setup.

These versions will work out of the box but iOS SDKs prior to 11.3 should also work. You can choose another installed version in default.nix

More recent Xcodes should also work, as long as one of the SDKs mentioned above has been used. To add another SDK to your current Xcode, download the corresponding Xcode, extract it and copy its SDK folder next to the installed one, e.g.

open -W Xcode_9.2.xip
sudo cp -R Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.2.sdk

You can verify that you have correct versions by running

xcodebuild -showsdks
Certificates

To deploy and/or package apps, you'll need to inform Apple of your development devices and permissions by adding credentials to the correct provisioning profile via the Apple Developer portal.

  1. Open up XCode and go to Preferences - Accounts. Select the organization Member role, click Manage Certificates, and add an iOS Development certificate.
  2. Go to developer portal - devices and add your device. To find your device's UDID, select it in iTunes and click the serial number.
  3. Go to developer portal - development profiles. Create a development profile and add your certificate and device. Click "Generate" and then download and open the profile.

Building

  1. In your project's default.nix set values for ios.bundleIdentifier and ios.bundleName. Ensure that bundleIdentifier matches the App ID of the development profile, or that you are using a wildcard profile.
  2. Run nix-build -A ios.frontend -o result-ios to build the app. Find it at result-ios/frontend.app

Deploying

  1. Connect the registered iPhone.
  2. Find your Apple Team ID in the developer portal.
  3. Run the deploy command with your Team ID:
result-ios/bin/deploy [TEAM_ID]
# or in debug mode via lldb:
result-ios/bin/deploy [TEAM_ID] -d

Packaging

  1. Go to developer portal - distribution profiles. Create and download a distribution profile.
  2. Run the package script with your TEAM ID and your distribution profile to create a .ipa:
result-ios/bin/package [TEAM_ID] /path/to/output/.ipa /path/to/profile/file

Debugging

It's also possible to inspect iOS WkWebView apps once they are installed in the iPhone:

  1. On the desktop, go to Safari > Preferences > Advanced and enable Develop menu.
  2. On the iPhone go to Settings > Safari > Advanced and enable Web Inspector.
  3. Open the app on the iPhone while it is connected to the desktop.
  4. In the desktop's Safari Develop menu, you should see your iPhone. Select the screen under the name of the app.

Android

NOTE: Currently Android builds are only supported on Linux.

  1. In your project's default.nix set a suitable value for android.applicationId and android.displayName.
  2. In your project's default.nix pass config.android_sdk.accept_license = true; in the arguments to the import of of .obelisk/impl to indicate your acceptance of the Android Software Development Kit License Agreement, which is required to build Android apps.
  3. Run nix-build -A android.frontend -o result-android to build the Android app.
  4. A debug version of the app should be generated at result-android/android-app-debug.apk

Now deploy the built apk file to your Android device:

  1. Enable USB debugging in your Android device (instructions here)
  2. Connect the device using USB (be sure to confirm any security prompts on the device)
  3. Run the deploy script: result-android/bin/deploy

Alternatively, you can deploy from an obelisk deployment directory (a directory generated post ob deploy init ... command) using the ob deploy test android command. This command will accomplish the following:

  1. Create a key store and apk signing key (android_keystore.jks)
  2. Build a Signed Android apk for your application
  3. Deploy the Signed apk to your connected Android device

In the event that you change your key or keystore password, you will have to update your credentials within the JSON object found in android_keytool_config.json.

Additional documentation on Java key stores can be found here.

This should copy over and install the application on your device (if you see a "signatures do not match" error, simply uninstall the previous app from the device before retrying the deploy). The name of the installed application will be what you have specified for android.displayName in the default.nix.

Releasing to Play Store

Build a release version

After having configured signing for your app, you may proceed to build a release version of the app. This is no different to how you build the non-release version, so consult the section Android further above for exact instructions on building and deploying to your device.

Frequently Asked Questions (FAQ)

Refer to FAQ.

Contributing

Contributions and issue reports are encouraged and appreciated! Refer to the Contributing guide for information about getting started.

More Repositories

1

ledger-app-tezos

Ledger app for Tezos
C
102
star
2

dependent-map

Dependently-typed finite maps (partial dependent products)
Haskell
63
star
3

ipfs-nix-guide

IPFS × Nix Guide
62
star
4

dependent-sum

Dependent sums and supporting typeclasses for comparing and displaying them
Haskell
54
star
5

nix-thunk

seamless management of source dependencies with nix
Haskell
38
star
6

hydra-pay

An open-source library for Cardano developers using Hydra (Head)
Haskell
38
star
7

rhyolite

Haskell
26
star
8

aeson-gadt-th

Template Haskell for generating ToJSON and FromJSON instances for GADTs
Haskell
22
star
9

beam-automigrate

Generated migrations for beam databases
Haskell
20
star
10

gargoyle

A framework for managing daemons from Haskell and libraries for use with postgresql and nix
Haskell
18
star
11

vessel

Functor-parametric containers
Haskell
18
star
12

solana-bridges

Rust
17
star
13

haven

Use haskell to produce a nix set of maven dependencies!
Haskell
12
star
14

obelisk-oauth

Haskell
12
star
15

haveibeenpwned

Haskell library that uses HIBP to evaluate passwords
Haskell
11
star
16

ledger-app-nervos

C
10
star
17

directory-contents

Recursively build a tree of directory contents, avoiding symlink cycles
Haskell
10
star
18

calculator-tutorial

Building a calculator with Reflex-FRP
CSS
10
star
19

go-ipfs-swh-plugin

IPFS plugin for SoftWare Heritage IDentifiers
Go
9
star
20

constraints-extras

Convenience functions and template haskell for working with constraints
Haskell
9
star
21

obelisk-systemd

Turn your obelisk app into a systemd service
Nix
9
star
22

daml-cucumber

Cucumber testing for daml scripts
Haskell
8
star
23

ledger-app-avalanche

Ledger app providing an AVAX wallet
C
7
star
24

obelisk-tailwind-example

An example project using Obelisk, PostCSS, and Tailwind CSS
Nix
7
star
25

android-activity

Java
7
star
26

opam-nixify

OCaml
7
star
27

prim-uniq

Opaque unique identifiers in primitive state monads
Haskell
6
star
28

cross-android

Nix
5
star
29

avalanche-wallet-cli

JavaScript
4
star
30

which

Haskell
4
star
31

commutative-semigroups

Haskell
4
star
32

monoid-map

Newtype wrapper around 'Data.Map.Monoidal.MonoidalMap' that has a correct 'Group' instance.
Haskell
4
star
33

nervos-force-bridge

Nix
4
star
34

echarts-jsdom

Haskell
4
star
35

dependent-monoidal-map

Data.Dependent.Map variant that appends conflicting entries when merging maps instead of discarding one side of the conflict
Haskell
4
star
36

polygon-nix

Deploy a Polygon Node with Nix instead of Ansible
Nix
4
star
37

ledger-app-sui

Rust
3
star
38

dependent-sum-aeson-orphans

JSON instances for DSum and DMap
Haskell
3
star
39

reflex-dom-echarts

Haskell
3
star
40

cli-extras

Haskell
3
star
41

socket-over-tls-service

A NixOS service that forwards a Unix domain socket over TLS
Nix
3
star
42

ghcjs-json

Haskell
3
star
43

push-notifications

Haskell
3
star
44

seven-guis-vty

7GUIs implemented in reflex-vty
Nix
3
star
45

fomantic-sketch-ui-kit

A Sketch library for Fomantic UI (Semantic UI's maintained successor)
3
star
46

react

Haskell
3
star
47

ledger-app-pocket

Rust
3
star
48

obelisk-examples

Examples of full-stack applications using Obelisk
Haskell
3
star
49

ledger-app-provenance

Rust
2
star
50

ledger-platform

Infrastructure for writing ledger apps, built with Nix
Nix
2
star
51

cli-nix

Haskell
2
star
52

hw-app-avalanche

LedgerJS bindings for Avalanche
JavaScript
2
star
53

plaid

Haskell client for Plaid
Haskell
2
star
54

obelisk-google-analytics

Haskell
2
star
55

reflex-react-example

Nix
2
star
56

lazy-minting

Smart contract implementation allowing lazy minting and auctioning capability of NFTs
2
star
57

reflex-dom-plaid

Haskell
2
star
58

lazy-minting-marketplace

Solidity
2
star
59

hydra-head-demo

Haskell
2
star
60

daml-syndicated-loan

Syndicated loan example implemented in DAML
Shell
2
star
61

monad-logger-extras

Build composable logging backends for monad-logger
Haskell
2
star
62

hs-openmoji-data

OpenMoji for Haskell
Haskell
2
star
63

hw-app-kda

TypeScript
1
star
64

remote-iserv

Libraries for remote implementations of the GHC external interpreter
Haskell
1
star
65

postgresql-lo-stream

Library for streaming large objects to and from PostgreSQL in Haskell
Haskell
1
star
66

snap-stream

Snap handlers for streaming access with range requests
Haskell
1
star
67

optimism-znft

Nix
1
star
68

logging-effect-syslog

Haskell
1
star
69

ledger-app-kadena

TypeScript
1
star
70

dependent-sum-universe-orphans

Haskell
1
star
71

logging-effect-colors

Haskell
1
star
72

beam-migrate-temp

Haskell
1
star
73

database-id

Haskell
1
star
74

dombuilder-pandoc

Haskell
1
star
75

terrad-nix

Deploy a terrad node with NixOS
Nix
1
star
76

partial-map

Haskell
1
star
77

dependent-sum-template

Template Haskell code to generate instances of classes in some package
Haskell
1
star
78

reflex-webauthn

Reflex based implementation of webauthn
Haskell
1
star
79

hw-app-pokt

TypeScript
1
star
80

hw-app-obsidian-common

TypeScript
1
star
81

bytestring-aeson-orphans

Aeson instances for ByteString, using base 64 encoding
Haskell
1
star
82

hnix-debug

Haskell
1
star
83

gitea-api

Client for Gitea API, generated using the OpenAPI haskell-http-client generator
Haskell
1
star
84

nix-daml-sdk

Nix
1
star
85

obelisk-https

Serve Obelisk applications securely
Nix
1
star