---
# System prepended metadata

title: Artefact Integrity
tags: [in-review]

---

# Artefact Integrity

**Author**: gak
**Status**: draft / ==in-review== / accepted / implemented / rejected

Related issue and copied some text Alex's description: https://github.com/TBD54566975/ftl/issues/2036

## Description

When deploying modules to the controller and runners, require an integrity check of the bundle with signed digest that is validated with a pre-installed public key.

## Motivation

Integrity checking as part of the upload step, ensures that only artefacts from authorized sources (sources with access to the appropriate signing key) are able to install new artefacts into the artefact store. This provides early feedback to developers and intrusion detection systems that the attempted action is unauthorized. This also provides a means of establishing trust that deployable artefacts were generated via trusted means (for example a CI pipeline).

## Goals

- Opt-in to require signing/checking of all artefacts. By default it would not be required.
- Support private keys with passwords (?)
- ~~Support a path option with a default private key path, e.g. ftl deploy `--integrity-key-path ./int.private.pem`~~
- ~~Support 1Password fetching a private key via `ftl-project.toml` reference or the cli: `--integrity-key-1password vault entry`~~
- Access integrity from the secrets system, e.g `ftl deploy --1password --vault FTL --integrity-secret integrity.pem`

## Questions

Q: should private keys support passwords?
Q: should the schema be signed too?

## Design (how)

Runners must be deployed with the appropriate public key, as getting it from the
controller will undermine the signature.

### Required changes

```
type DeploymentArtefact struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Digest     string `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"`
	Path       string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
	Executable bool   `protobuf:"varint,3,opt,name=executable,proto3" json:"executable,omitempty"`
    
    # add tink signature
}
```

- Use ECDSA-P256-SHA256 only initially with support for others (via saving the algo in the signature)
- CLI/args to specify public key, enabling integrity for controller and runner.
    - FTL_INTEGRITY_PUBLIC_KEY_PATH
    - ~~FTL_INTEGRITY_PUBLIC_KEY_SECRET="op://vault/integrity.pem"~~
        - This is probably not safe since the secret is given by the controller
    
- Building:
    - cli arg or env pointing to private key file eg `integrity.key`
        - FTL_INTEGRITY_PRIVATE_KEY_PATH="/mnt/integrity.key"
        - FTL_INTEGRITY_PRIVATE_KEY_SECRET="op://vault/integrity.key"
    - For each artefact (e.g `main` or `classpath.txt`)
        - create a signature file `classpath.txt.sig` next to the file with.

- UploadArtefact (client to controller)
    - Client detects a .sig file when uploading and passes it into `UploadArtefactRequest`
    - Controller to check then save signature in DB, if enabled.
        - Schema migration!
    - Warn if disabled but received a signature
    - If the signature check fails, report an error and don't accept the upload.
    
- GetDeploymentArtefacts (runner pull from controller)
    - The runner stores the public key as part of the deployment.
    - The runner should accept an arg or env var of the path to the public key.
    - Client downloads an artefact via deployment key, which optionally contains a signature.
    - If the client is configured to check integrity, validate against the public key.
    - Warn if disabled but received a signature
    - If the signature check fails, report an error.

