diff --git a/.github/workflows/cargo_audit.yml b/.github/workflows/cargo_audit.yml
new file mode 100644
index 0000000000000000000000000000000000000000..94b00f6ea242df015e54d4cb33abfb4be1041183
--- /dev/null
+++ b/.github/workflows/cargo_audit.yml
@@ -0,0 +1,24 @@
+name: Security audit
+on:
+  schedule:
+    - cron: '0 0 * * *'
+jobs:
+  audit:
+    runs-on: ubuntu-18.04
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          toolchain: nightly
+          target: thumbv7em-none-eabi
+          override: true
+      - uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+      - name: Install Python dependencies
+        run: python -m pip install --upgrade pip setuptools wheel
+      - name: Set up OpenSK
+        run: ./setup.sh
+      - uses: actions-rs/audit-check@v1
+        with:
+          token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/cargo_check.yml b/.github/workflows/cargo_check.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9697b373bb24d8e85e1a3ea745d5baa3fa6cce32
--- /dev/null
+++ b/.github/workflows/cargo_check.yml
@@ -0,0 +1,61 @@
+name: Cargo check
+on:
+  push:
+    paths:
+      - 'examples/*.rs'
+      - 'libraries/**/*.rs'
+      - 'src/**/*.rs'
+      - 'patches/**'
+      - '**/Cargo.toml'
+      - '.cargo/config'
+      - '!third_party/**'
+  pull_request:
+    types: [opened, synchronize, reopened]
+
+jobs:
+  cargo_check:
+    runs-on: ubuntu-18.04
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          toolchain: nightly
+          target: thumbv7em-none-eabi
+          override: true
+      - uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+      - name: Install Python dependencies
+        run: python -m pip install --upgrade pip setuptools wheel
+      - name: Set up OpenSK
+        run: ./setup.sh
+
+      - name: Check OpenSK w/o features
+        uses: actions-rs/cargo@v1
+        with:
+          command: check
+          args: --target thumbv7em-none-eabi --release
+
+      - name: Check OpenSK with_ctap1
+        uses: actions-rs/cargo@v1
+        with:
+          command: check
+          args: --target thumbv7em-none-eabi --release --features with_ctap1
+
+      - name: Check OpenSK debug_ctap
+        uses: actions-rs/cargo@v1
+        with:
+          command: check
+          args: --target thumbv7em-none-eabi --release --features debug_ctap
+
+      - name: Check OpenSK debug_ctap,with_ctap1
+        uses: actions-rs/cargo@v1
+        with:
+          command: check
+          args: --target thumbv7em-none-eabi --release --features debug_ctap,with_ctap1
+
+      - name: Check examples
+        uses: actions-rs/cargo@v1
+        with:
+          command: check
+          args: --target thumbv7em-none-eabi --release --examples
diff --git a/.github/workflows/cargo_fmt.yml b/.github/workflows/cargo_fmt.yml
new file mode 100644
index 0000000000000000000000000000000000000000..324b4755c7fbd5975bb7e8eea2684fe3c75eea5d
--- /dev/null
+++ b/.github/workflows/cargo_fmt.yml
@@ -0,0 +1,49 @@
+name: Cargo format
+on:
+  push:
+    paths:
+      - 'examples/*.rs'
+      - 'libraries/**/*.rs'
+      - 'src/**/*.rs'
+      - 'patches/**'
+      - '**/Cargo.toml'
+      - '.cargo/config'
+      - '!third_party/**'
+  pull_request:
+    types: [opened, synchronize, reopened]
+
+jobs:
+  cargo_format:
+    runs-on: ubuntu-18.04
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          toolchain: nightly
+          target: thumbv7em-none-eabi
+          override: true
+      - uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+      - name: Install Python dependencies
+        run: python -m pip install --upgrade pip setuptools wheel
+      - name: Set up OpenSK
+        run: ./setup.sh
+
+      - name: Cargo format src/
+        uses: actions-rs/cargo@v1
+        with:
+          command: fmt
+          args: --all -- --check
+
+      - name: Cargo format libraries/cbor
+        uses: actions-rs/cargo@v1
+        with:
+          command: fmt
+          args: --manifest-path libraries/cbor/Cargo.toml --all -- --check
+
+      - name: Cargo format libraries/crypto
+        uses: actions-rs/cargo@v1
+        with:
+          command: fmt
+          args: --manifest-path libraries/crypto/Cargo.toml --all -- --check
diff --git a/.github/workflows/mdlint.yml b/.github/workflows/mdlint.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c697e0c3422041fa50b7b23f6836b9a8847abccd
--- /dev/null
+++ b/.github/workflows/mdlint.yml
@@ -0,0 +1,21 @@
+name: markdownlint
+on:
+  push:
+    paths:
+      - '**/*.md'
+      - '.markdownlint.json'
+      - '!third_party/**'
+  pull_request:
+    types: [opened, synchronize, reopened]
+
+jobs:
+  mdlint:
+    runs-on: ubuntu-18.04
+    steps:
+      - uses: actions/checkout@v2
+      - name: markdownlint-cli
+        uses: nosborn/github-action-markdown-cli@v1.1.1
+        with:
+          files: '**/*.md'
+          ignore_files: "third_party/*"
+          config_file: ".markdownlint.json"
diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml
new file mode 100644
index 0000000000000000000000000000000000000000..dc739b8b71234fd0a7e507e3e8ae4a774eaaf218
--- /dev/null
+++ b/.github/workflows/python.yml
@@ -0,0 +1,44 @@
+name: pylint
+on:
+  push:
+    paths:
+      - '**/*.py'
+      - '.pylintrc'
+      - '!third_party/**'
+  pull_request:
+    types: [opened, synchronize, reopened]
+jobs:
+  pylint:
+    runs-on: ubuntu-18.04
+    strategy:
+      matrix:
+        python-version: [3.6, 3.7, 3.8]
+    steps:
+      - uses: actions/checkout@v2
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v1
+        with:
+          python-version: ${{ matrix.python-version }}
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip setuptools wheel
+          pip install tockloader
+      - name: Test code with pylint
+        run: |
+          pip install pylint
+          pylint --rcfile=.pylintrc --score=n `find . ! -path "./third_party/*" -type f -name '*.py'`
+
+  yapf:
+    runs-on: ubuntu-18.04
+    steps:
+      - uses: actions/checkout@v2
+      - name: Set up Python 3.7
+        uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip setuptools wheel
+          pip install yapf
+      - name: Test code formatting with yapf
+        run: yapf --style=chromium --recursive --exclude third_party --diff .
diff --git a/README.md b/README.md
index e288de0e0c46643cef1e1f221afc461a9e57980a..34d7a9bedb7dde5b5c493fd783e20e4f0d78eec9 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@
 # <img alt="OpenSK logo" src="docs/img/OpenSK.svg" width="200px">
 
 [![Build Status](https://travis-ci.org/google/OpenSK.svg?branch=master)](https://travis-ci.org/google/OpenSK)
+![markdownlint](https://github.com/google/OpenSK/workflows/markdownlint/badge.svg?branch=master)
+![pylint](https://github.com/google/OpenSK/workflows/pylint/badge.svg?branch=master)
+![Cargo check](https://github.com/google/OpenSK/workflows/Cargo%20check/badge.svg?branch=master)
+![Cargo format](https://github.com/google/OpenSK/workflows/Cargo%20format/badge.svg?branch=master)
 
 ## OpenSK
 
diff --git a/docs/install.md b/docs/install.md
index 9eff4fdf34486579c371b89d447dd69dc380d5a6..59bb7c4b1dd018b2cb69ae8010b5cdf68e26d3e3 100644
--- a/docs/install.md
+++ b/docs/install.md
@@ -43,7 +43,7 @@ This guide **does not** cover how to setup the JTAG probe on your system.
 In order to compile and flash a working OpenSK firmware, you will need the
 following:
 
-*   rustup (can be installed with https://rustup.rs/)
+*   rustup (can be installed with [Rustup](https://rustup.rs/))
 *   python3 and pip
 *   the OpenSSL command line tool
 
@@ -329,14 +329,16 @@ If you have issues with the demo website, the following commands should help you
 understand whether OpenSK was installed properly.
 
 When plugging in the USB key, the following line should appear in `lsusb`.
-```
+
+```shell
 $ lsusb
 ...
 Bus XXX Device YYY: ID 1915:521f Nordic Semiconductor ASA OpenSK
 ```
 
 You should also see lines similar to the following in `dmesg`.
-```
+
+```shell
 $ dmesg
 ...
 [XXX] usb A-BB: new full-speed USB device number 00 using xhci_hcd
diff --git a/setup.sh b/setup.sh
index ae0fd0cacaf8f118a9a5550a7a7ec3783d49fa95..5f9e63812082f8ea18c877c01be7a2c660871b84 100755
--- a/setup.sh
+++ b/setup.sh
@@ -13,6 +13,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# Ensure the script doesn't fail on Github workflows
+export TERM=${TERM:-vt100}
+done_text="$(tput bold)DONE.$(tput sgr0)"
+
 set -e
 
 # Check that rustup and pip3 are installed
@@ -29,8 +33,6 @@ check_command pip3
 # Ensure the submodules are pulled and up-to-date
 git submodule update --init
 
-done_text="$(tput bold)DONE.$(tput sgr0)"
-
 patch_conflict_detected () {
   cat <<EOF