mirror of
https://github.com/theupdateframework/python-tuf
synced 2026-05-24 10:08:28 +00:00
Merge pull request #849 from theupdateframework/clarify_role_of_cli
Reframe CLI, revise quickstart, and reorganize tutorials
This commit is contained in:
commit
4f01a31346
5 changed files with 312 additions and 235 deletions
220
docs/CLI.md
220
docs/CLI.md
|
|
@ -1,11 +1,15 @@
|
|||
# CLI #
|
||||
# Command-Line Interface #
|
||||
|
||||
The CLI requires a few dependencies and C extensions that can be installed with
|
||||
`pip install securesystemslib[crypto,pynacl]`.
|
||||
The TUF command-line interface (CLI) requires a full
|
||||
[TUF installation](INSTALLATION.rst). Be sure to include the installation of
|
||||
extra dependencies and C extensions (
|
||||
```pip install securesystemslib[crypto,pynacl]```).
|
||||
|
||||
[CLI_EXAMPLES.md](CLI_EXAMPLES.md) covers more complex examples.
|
||||
The use of the CLI is documented with examples below.
|
||||
|
||||
----
|
||||
# Basic Examples #
|
||||
|
||||
## Create a repository ##
|
||||
|
||||
Create a TUF repository in the current working directory. A cryptographic key
|
||||
|
|
@ -235,3 +239,211 @@ $ repo.py --clean
|
|||
$ repo.py --clean --path </path/to/dirty/repo>
|
||||
```
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Further Examples #
|
||||
|
||||
## Basic Update Delivery ##
|
||||
|
||||
Steps:
|
||||
|
||||
(1) initialize a repo.
|
||||
|
||||
(2) delegate trust of target files to another role.
|
||||
|
||||
(3) add a trusted file to the delegated role.
|
||||
|
||||
(4) fetch the trusted file from the delegated role.
|
||||
|
||||
```Bash
|
||||
Step (1)
|
||||
$ repo.py --init
|
||||
|
||||
Step (2)
|
||||
$ repo.py --key ed25519 --filename mykey
|
||||
$ repo.py --delegate "README.*" --delegatee myrole --pubkeys tufkeystore/mykey.pub
|
||||
$ repo.py --sign tufkeystore/mykey --role myrole
|
||||
Enter a password for the encrypted key (tufkeystore/mykey):
|
||||
$ echo "my readme text" > README.txt
|
||||
|
||||
Step (3)
|
||||
$ repo.py --add README.txt --role myrole --sign tufkeystore/mykey
|
||||
Enter a password for the encrypted key (tufkeystore/mykey):
|
||||
```
|
||||
|
||||
Serve the repo
|
||||
```Bash
|
||||
$ cd tufrepo/
|
||||
$ python -m SimpleHTTPServer 8001
|
||||
```
|
||||
|
||||
```Bash
|
||||
Step (4)
|
||||
$ client.py --repo http://localhost:8001 README.txt
|
||||
$ tree .
|
||||
.
|
||||
├── tuf.log
|
||||
├── tufrepo
|
||||
│ └── metadata
|
||||
│ ├── current
|
||||
│ │ ├── 1.root.json
|
||||
│ │ ├── myrole.json
|
||||
│ │ ├── root.json
|
||||
│ │ ├── snapshot.json
|
||||
│ │ ├── targets.json
|
||||
│ │ └── timestamp.json
|
||||
│ └── previous
|
||||
│ ├── 1.root.json
|
||||
│ ├── root.json
|
||||
│ ├── snapshot.json
|
||||
│ ├── targets.json
|
||||
│ └── timestamp.json
|
||||
└── tuftargets
|
||||
└── README.txt
|
||||
|
||||
5 directories, 13 files
|
||||
```
|
||||
|
||||
|
||||
## Correcting a Key ##
|
||||
The filename of the top-level keys must be "root_key," "targets_key,"
|
||||
"snapshot_key," and "root_key." The filename can vary for any additional
|
||||
top-level key.
|
||||
|
||||
Steps:
|
||||
|
||||
(1) initialize a repo containing default keys for the top-level roles.
|
||||
(2) distrust the default key for the root role.
|
||||
(3) create a new key and trust its use with the root role.
|
||||
(4) sign the root metadata file.
|
||||
|
||||
```Bash
|
||||
Step (1)
|
||||
$ repo.py --init
|
||||
|
||||
Step (2)
|
||||
$ repo.py --distrust --pubkeys tufkeystore/root_key.pub --role root
|
||||
|
||||
Step (3)
|
||||
$ repo.py --key ed25519 --filename root_key
|
||||
$ repo.py --trust --pubkeys tufkeystore/root_key.pub --role root
|
||||
|
||||
Step (4)
|
||||
$ repo.py --sign tufkeystore/root_key --role root
|
||||
Enter a password for the encrypted key (tufkeystore/root_key):
|
||||
```
|
||||
|
||||
|
||||
## More Update Delivery ##
|
||||
|
||||
Steps:
|
||||
|
||||
(1) create a bare repo.
|
||||
|
||||
(2) add keys to the top-level roles.
|
||||
|
||||
(3) delegate trust of particular target files to another role X, where role X
|
||||
has a signature threshold 2 and is marked as a terminating delegation. The
|
||||
keys for role X and Y should be created prior to performing the delegation.
|
||||
|
||||
(4) Delegate from role X to role Y.
|
||||
|
||||
(5) have role X sign for a file also signed by the Targets role, to demonstrate
|
||||
the expected file that should be downloaded by the client.
|
||||
|
||||
(6) perform an update.
|
||||
|
||||
(7) halt the server, add README.txt to the Targets role, restart the server,
|
||||
and fetch the Target's role README.txt.
|
||||
|
||||
(8) Add LICENSE to 'role_y' and demonstrate that the client must not fetch it
|
||||
because 'role_x' is a terminating delegation (and hasn't signed for it).
|
||||
|
||||
```Bash
|
||||
Steps (1) and (2)
|
||||
$ repo.py --init --consistent --bare
|
||||
$ repo.py --key ed25519 --filename root_key
|
||||
$ repo.py --trust --pubkeys tufkeystore/root_key.pub --role root
|
||||
$ repo.py --key ecdsa --filename targets_key
|
||||
$ repo.py --trust --pubkeys tufkeystore/targets_key.pub --role targets
|
||||
$ repo.py --key rsa --filename snapshot_key
|
||||
$ repo.py --trust --pubkeys tufkeystore/snapshot_key.pub --role snapshot
|
||||
$ repo.py --key ecdsa --filename timestamp_key
|
||||
$ repo.py --trust --pubkeys tufkeystore/timestamp_key.pub --role timestamp
|
||||
$ repo.py --sign tufkeystore/root_key --role root
|
||||
Enter a password for the encrypted key (tufkeystore/root_key):
|
||||
$ repo.py --sign tufkeystore/targets_key --role targets
|
||||
Enter a password for the encrypted key (tufkeystore/targets_key):
|
||||
```
|
||||
|
||||
```Bash
|
||||
Steps (3) and (4)
|
||||
$ repo.py --key ed25519 --filename key_x
|
||||
$ repo.py --key ed25519 --filename key_x2
|
||||
|
||||
$ repo.py --delegate "README.*" "LICENSE" --delegatee role_x --pubkeys
|
||||
tufkeystore/key_x.pub tufkeystore/key_x2.pub --threshold 2 --terminating
|
||||
$ repo.py --sign tufkeystore/key_x tufkeystore/key_x2 --role role_x
|
||||
|
||||
$ repo.py --key ed25519 --filename key_y
|
||||
|
||||
$ repo.py --delegate "README.*" "LICENSE" --delegatee role_y --role role_x
|
||||
--pubkeys tufkeystore/key_y.pub --sign tufkeystore/key_x tufkeystore/key_x2
|
||||
|
||||
$ repo.py --sign tufkeystore/key_y --role role_y
|
||||
```
|
||||
|
||||
```Bash
|
||||
Steps (5) and (6)
|
||||
$ echo "role_x's readme" > README.txt
|
||||
$ repo.py --add README.txt --role role_x --sign tufkeystore/key_x tufkeystore/key_x2
|
||||
```
|
||||
|
||||
Serve the repo
|
||||
```Bash
|
||||
$ cd tufrepo/
|
||||
$ python -m SimpleHTTPServer 8001
|
||||
```
|
||||
|
||||
Fetch the role x's README.txt
|
||||
```Bash
|
||||
$ client.py --repo http://localhost:8001 README.txt
|
||||
$ cat tuftargets/README.txt
|
||||
role_x's readme
|
||||
```
|
||||
|
||||
|
||||
```Bash
|
||||
Step (7)
|
||||
halt the server...
|
||||
|
||||
$ echo "Target role's readme" > README.txt
|
||||
$ repo.py --add README.txt
|
||||
|
||||
restart the server...
|
||||
```
|
||||
|
||||
```Bash
|
||||
$ rm -rf tuftargets/ tuf.log
|
||||
$ client.py --repo http://localhost:8001 README.txt
|
||||
$ cat tuftargets/README.txt
|
||||
Target role's readme
|
||||
```
|
||||
|
||||
```Bash
|
||||
Step (8)
|
||||
$ echo "role_y's license" > LICENSE
|
||||
$ repo.py --add LICENSE --role role_y --sign tufkeystore/key_y
|
||||
```
|
||||
|
||||
```Bash
|
||||
$ rm -rf tuftargets/ tuf.log
|
||||
$ client.py --repo http://localhost:8001 LICENSE
|
||||
Error: 'LICENSE' not found.
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,204 +0,0 @@
|
|||
# CLI Usage Examples #
|
||||
|
||||
This document contains a few examples of creating repositories with the CLI.
|
||||
The sections below correspond with a different example, and each begins with an
|
||||
outline of the steps to be followed by the user.
|
||||
|
||||
## A basic example ##
|
||||
|
||||
Steps:
|
||||
|
||||
(1) initialize a repo.
|
||||
|
||||
(2) delegate trust of target files to another role.
|
||||
|
||||
(3) add a trusted file to the delegated role.
|
||||
|
||||
(4) fetch the trusted file from the delegated role.
|
||||
|
||||
```Bash
|
||||
Step (1)
|
||||
$ repo.py --init
|
||||
|
||||
Step (2)
|
||||
$ repo.py --key ed25519 --filename mykey
|
||||
$ repo.py --delegate "README.*" --delegatee myrole --pubkeys tufkeystore/mykey.pub
|
||||
$ repo.py --sign tufkeystore/mykey --role myrole
|
||||
Enter a password for the encrypted key (tufkeystore/mykey):
|
||||
$ echo "my readme text" > README.txt
|
||||
|
||||
Step (3)
|
||||
$ repo.py --add README.txt --role myrole --sign tufkeystore/mykey
|
||||
Enter a password for the encrypted key (tufkeystore/mykey):
|
||||
```
|
||||
|
||||
Serve the repo
|
||||
```Bash
|
||||
$ cd tufrepo/
|
||||
$ python -m SimpleHTTPServer 8001
|
||||
```
|
||||
|
||||
```Bash
|
||||
Step (4)
|
||||
$ client.py --repo http://localhost:8001 README.txt
|
||||
$ tree .
|
||||
.
|
||||
├── tuf.log
|
||||
├── tufrepo
|
||||
│ └── metadata
|
||||
│ ├── current
|
||||
│ │ ├── 1.root.json
|
||||
│ │ ├── myrole.json
|
||||
│ │ ├── root.json
|
||||
│ │ ├── snapshot.json
|
||||
│ │ ├── targets.json
|
||||
│ │ └── timestamp.json
|
||||
│ └── previous
|
||||
│ ├── 1.root.json
|
||||
│ ├── root.json
|
||||
│ ├── snapshot.json
|
||||
│ ├── targets.json
|
||||
│ └── timestamp.json
|
||||
└── tuftargets
|
||||
└── README.txt
|
||||
|
||||
5 directories, 13 files
|
||||
```
|
||||
|
||||
|
||||
## An example of replacing a top-level key ##
|
||||
The filename of the top-level keys must be "root_key," "targets_key,"
|
||||
"snapshot_key," and "root_key." The filename can vary for any additional
|
||||
top-level key.
|
||||
|
||||
Steps:
|
||||
|
||||
(1) initialize a repo containing default keys for the top-level roles.
|
||||
(2) distrust the default key for the root role.
|
||||
(3) create a new key and trust its use with the root role.
|
||||
(4) sign the root metadata file.
|
||||
|
||||
```Bash
|
||||
Step (1)
|
||||
$ repo.py --init
|
||||
|
||||
Step (2)
|
||||
$ repo.py --distrust --pubkeys tufkeystore/root_key.pub --role root
|
||||
|
||||
Step (3)
|
||||
$ repo.py --key ed25519 --filename root_key
|
||||
$ repo.py --trust --pubkeys tufkeystore/root_key.pub --role root
|
||||
|
||||
Step (4)
|
||||
$ repo.py --sign tufkeystore/root_key --role root
|
||||
Enter a password for the encrypted key (tufkeystore/root_key):
|
||||
```
|
||||
|
||||
|
||||
## A more complicated example ##
|
||||
|
||||
Steps:
|
||||
|
||||
(1) create a bare repo.
|
||||
|
||||
(2) add keys to the top-level roles.
|
||||
|
||||
(3) delegate trust of particular target files to another role X, where role X
|
||||
has a signature threshold 2 and is marked as a terminating delegation. The
|
||||
keys for role X and Y should be created prior to performing the delegation.
|
||||
|
||||
(4) Delegate from role X to role Y.
|
||||
|
||||
(5) have role X sign for a file also signed by the Targets role, to demonstrate
|
||||
the expected file that should be downloaded by the client.
|
||||
|
||||
(6) perform an update.
|
||||
|
||||
(7) halt the server, add README.txt to the Targets role, restart the server,
|
||||
and fetch the Target's role README.txt.
|
||||
|
||||
(8) Add LICENSE to 'role_y' and demonstrate that the client must not fetch it
|
||||
because 'role_x' is a terminating delegation (and hasn't signed for it).
|
||||
|
||||
```Bash
|
||||
Steps (1) and (2)
|
||||
$ repo.py --init --consistent --bare
|
||||
$ repo.py --key ed25519 --filename root_key
|
||||
$ repo.py --trust --pubkeys tufkeystore/root_key.pub --role root
|
||||
$ repo.py --key ecdsa --filename targets_key
|
||||
$ repo.py --trust --pubkeys tufkeystore/targets_key.pub --role targets
|
||||
$ repo.py --key rsa --filename snapshot_key
|
||||
$ repo.py --trust --pubkeys tufkeystore/snapshot_key.pub --role snapshot
|
||||
$ repo.py --key ecdsa --filename timestamp_key
|
||||
$ repo.py --trust --pubkeys tufkeystore/timestamp_key.pub --role timestamp
|
||||
$ repo.py --sign tufkeystore/root_key --role root
|
||||
Enter a password for the encrypted key (tufkeystore/root_key):
|
||||
$ repo.py --sign tufkeystore/targets_key --role targets
|
||||
Enter a password for the encrypted key (tufkeystore/targets_key):
|
||||
```
|
||||
|
||||
```Bash
|
||||
Steps (3) and (4)
|
||||
$ repo.py --key ed25519 --filename key_x
|
||||
$ repo.py --key ed25519 --filename key_x2
|
||||
|
||||
$ repo.py --delegate "README.*" "LICENSE" --delegatee role_x --pubkeys
|
||||
tufkeystore/key_x.pub tufkeystore/key_x2.pub --threshold 2 --terminating
|
||||
$ repo.py --sign tufkeystore/key_x tufkeystore/key_x2 --role role_x
|
||||
|
||||
$ repo.py --key ed25519 --filename key_y
|
||||
|
||||
$ repo.py --delegate "README.*" "LICENSE" --delegatee role_y --role role_x
|
||||
--pubkeys tufkeystore/key_y.pub --sign tufkeystore/key_x tufkeystore/key_x2
|
||||
|
||||
$ repo.py --sign tufkeystore/key_y --role role_y
|
||||
```
|
||||
|
||||
```Bash
|
||||
Steps (5) and (6)
|
||||
$ echo "role_x's readme" > README.txt
|
||||
$ repo.py --add README.txt --role role_x --sign tufkeystore/key_x tufkeystore/key_x2
|
||||
```
|
||||
|
||||
Serve the repo
|
||||
```Bash
|
||||
$ cd tufrepo/
|
||||
$ python -m SimpleHTTPServer 8001
|
||||
```
|
||||
|
||||
Fetch the role x's README.txt
|
||||
```Bash
|
||||
$ client.py --repo http://localhost:8001 README.txt
|
||||
$ cat tuftargets/README.txt
|
||||
role_x's readme
|
||||
```
|
||||
|
||||
|
||||
```Bash
|
||||
Step (7)
|
||||
halt the server...
|
||||
|
||||
$ echo "Target role's readme" > README.txt
|
||||
$ repo.py --add README.txt
|
||||
|
||||
restart the server...
|
||||
```
|
||||
|
||||
```Bash
|
||||
$ rm -rf tuftargets/ tuf.log
|
||||
$ client.py --repo http://localhost:8001 README.txt
|
||||
$ cat tuftargets/README.txt
|
||||
Target role's readme
|
||||
```
|
||||
|
||||
```Bash
|
||||
Step (8)
|
||||
$ echo "role_y's license" > LICENSE
|
||||
$ repo.py --add LICENSE --role role_y --sign tufkeystore/key_y
|
||||
```
|
||||
|
||||
```Bash
|
||||
$ rm -rf tuftargets/ tuf.log
|
||||
$ client.py --repo http://localhost:8001 LICENSE
|
||||
Error: 'LICENSE' not found.
|
||||
```
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
Getting Started
|
||||
---------------
|
||||
|
||||
- `Overview of TUF <OVERVIEW.rst>`_
|
||||
- `Installation <INSTALLATION.rst>`_
|
||||
- `Contributors <CONTRIBUTORS.rst>`_
|
||||
- `Quickstart <QUICKSTART.md>`_
|
||||
- `CLI <CLI.md>`_
|
||||
- `CLI Usage Examples <CLI_EXAMPLES.md>`_
|
||||
- `Tutorial <TUTORIAL.md>`_
|
||||
- Beginner Tutorials (using the basic command-line interface):
|
||||
- `Quickstart <QUICKSTART.md>`_
|
||||
- `CLI Documentation and Examples <CLI.md>`_
|
||||
- `Advanced Tutorial <TUTORIAL.md>`_
|
||||
- `Guidelines for Contributors <CONTRIBUTORS.rst>`_
|
||||
|
|
|
|||
|
|
@ -1,21 +1,48 @@
|
|||
# Quickstart #
|
||||
|
||||
The CLI requires a few dependencies and C extensions that can be installed with
|
||||
`pip install securesystemslib[crypto,pynacl]`.
|
||||
In this quickstart tutorial, we'll use the basic TUF command-line interface
|
||||
(CLI), which includes the `repo.py` script and the `client.py` script, to set
|
||||
up a repository with an update and metadata about that update, then download
|
||||
and verify that update as a client.
|
||||
|
||||
Unlike the underlying TUF modules that the CLI uses, the CLI itself is a bit
|
||||
bare-bones. Using the CLI is the easiest way to familiarize yourself with
|
||||
how TUF works, however. It will serve as a very basic update system.
|
||||
|
||||
----
|
||||
The following is a basic workflow in four steps:
|
||||
|
||||
**Step (1)** - Initialize a repo. The `tufrepo`, `tufkeystore`, and
|
||||
`tufclient` directories are created in the current working directory.
|
||||
**Step (0)** - Make sure TUF is installed.
|
||||
|
||||
Make sure that TUF is installed, along with some of the optional cryptographic
|
||||
libraries and C extensions. Try this command to do that:
|
||||
`pip install securesystemslib[crypto,pynacl] tuf`
|
||||
|
||||
If you run into errors during that pip command, please consult the more
|
||||
detailed [TUF Installation Instructions](INSTALLATION.rst). (There are some
|
||||
system libraries that you may need to install first.)
|
||||
|
||||
|
||||
**Step (1)** - Create a basic repository and client.
|
||||
|
||||
The following command will set up a basic update repository and basic client
|
||||
that knows about the repository. `tufrepo`, `tufkeystore`, and
|
||||
`tufclient` directories will be created in the current directory.
|
||||
|
||||
```Bash
|
||||
$ repo.py --init
|
||||
```
|
||||
Four sets of keys are created in the `tufkeystore` directory and metadata
|
||||
is initiated in the `tufrepo` and `tufclient` directories.
|
||||
|
||||
**Step (2)** - Add a target file to the repo. The file size and hashes of
|
||||
the target file are also written to the Targets metadata file.
|
||||
Four sets of keys are created in the `tufkeystore` directory. Initial metadata
|
||||
about the repository is created in the `tufrepo` directory, and also provided
|
||||
to the client in the `tufclient` directory.
|
||||
|
||||
|
||||
**Step (2)** - Add an update to the repository.
|
||||
|
||||
We'll create a target file that will later be delivered as an update to clients.
|
||||
Metadata about that file will be created and signed, and added to the
|
||||
repository's metadata.
|
||||
|
||||
```Bash
|
||||
$ echo 'Test file' > testfile
|
||||
$ repo.py --add testfile
|
||||
|
|
@ -38,21 +65,38 @@ tufrepo/
|
|||
|
||||
3 directories, 11 files
|
||||
```
|
||||
The new file `testfile` is added and metadata is updated in the `tufrepo` directory.
|
||||
|
||||
**Step (3)** - Serve the repo
|
||||
The new file `testfile` is added to the repository, and metadata is updated in
|
||||
the `tufrepo` directory. The Targets metadata (`targets.json`) now includes
|
||||
the file size and hashes of the `testfile` target file, and this metadata is
|
||||
signed by the Targets role's key, so that clients can verify that metadata
|
||||
about `testfile` and then verify `testfile` itself.
|
||||
|
||||
|
||||
**Step (3)** - Serve the repo.
|
||||
|
||||
We'll host a toy http server containing the `testfile` update and the
|
||||
repository's metadata.
|
||||
|
||||
```Bash
|
||||
$ cd "tufrepo/"
|
||||
$ python3 -m http.server 8001
|
||||
|
||||
# or, if you are using Python2:
|
||||
$ python -m SimpleHTTPServer 8001
|
||||
|
||||
or with Python 3...
|
||||
$ python3 -m http.server 8001
|
||||
```
|
||||
|
||||
**Step (4)** - Fetch a target file from the repo. The client downloads
|
||||
any required metadata and the requested target file.
|
||||
**Step (4)** - Obtain and verify the `testfile` update on a client.
|
||||
|
||||
The client can request the package `testfile` from the repository. TUF will
|
||||
download and verify metadata from the repository as necessary to determine
|
||||
what the trustworthy hashes and length of `testfile` are, then download
|
||||
the target `testfile` from the repository and keep it only if it matches that
|
||||
trustworthy metadata.
|
||||
|
||||
```Bash
|
||||
$ cd "tufclient/"
|
||||
$ cd "../tufclient/"
|
||||
$ client.py --repo http://localhost:8001 testfile
|
||||
$ tree
|
||||
.
|
||||
|
|
@ -75,11 +119,35 @@ $ tree
|
|||
|
||||
5 directories, 11 files
|
||||
```
|
||||
client.py verified metadata from the server and downloaded content. The client has now verified and obtained `testfile`.
|
||||
The scope of TUF ends here.
|
||||
|
||||
Now that a trustworthy update target has been obtained, an updater can proceed
|
||||
however it normally would to install or use the update.
|
||||
|
||||
----
|
||||
|
||||
See [CLI.md](CLI.md) and [CLI_EXAMPLES.md](CLI_EXAMPLES.md) to learn about the
|
||||
other supported CLI options. A [tutorial](TUTORIAL.md) is also available, and
|
||||
intended for users that want more control over the repo creation process.
|
||||
### Next Steps
|
||||
|
||||
TUF provides functionality for both ends of a software update system, the
|
||||
**update provider** and the **update client**.
|
||||
|
||||
`repo.py` made use of `tuf.repository_tool`'s functionality for an update
|
||||
provider, helping you produce and sign metadata about your updates.
|
||||
|
||||
`client.py` made use of `tuf.client.updater`'s client-side functionality,
|
||||
performing download and the critical verification steps for metadata and the
|
||||
update itself.
|
||||
|
||||
You can look at [CLI.md](CLI.md) to toy with the TUF CLI a bit more.
|
||||
After that, try out using the underlying modules for a great deal more control.
|
||||
The more detailed [Advanced Tutorial](TUTORIAL.md) shows you how to use the
|
||||
underlying modules, `repository_tool` and `updater`.
|
||||
|
||||
Ultimately, a sophisticated update client will use or re-implement those
|
||||
underlying modules. The TUF design is intended to play well with any update
|
||||
workflow.
|
||||
|
||||
Please provide feedback or questions for this or other tutorials, or
|
||||
TUF in general, by checking out
|
||||
[our contact info](https://github.com/theupdateframework/tuf#contact), or
|
||||
creating [issues](https://github.com/theupdateframework/tuf/issues) in this
|
||||
repository!
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Tutorial #
|
||||
# Advanced Tutorial #
|
||||
|
||||
## Table of Contents ##
|
||||
- [How to Create and Modify a TUF Repository](#how-to-create-and-modify-a-tuf-repository)
|
||||
|
|
|
|||
Loading…
Reference in a new issue