Goal

The goal for today is to start implementation of the Obnam server HTTP API. If all goes well, after this development session I can run the server, and use authenticated requests to upload chunks, download chunks, delete chunks, and search for chunks based on label.

Plan

  • I'll use the axum crate for the HTTP API. I've played with it a little bit, and it's pretty convenient to use.

  • I will keep the client and server as separate programs. There's no need to have them be the same one. I may later split the obnam crate, but for now everything can live in one crate.

  • The server will have its own configuration file. It can default to ~/.config/obnam/server.yaml to be separate from the client one. The server configuration contains, at minimum, the PASETO key pair for signing tokens, or the location where it is.

  • The server admin will be able to create an API token for a client using something like obnam-server token. For now, the clients are not distinguished except by token. Later on, clients will need their own IDs, I assume, but for now I'll only deal with tokens. When clients get IDs, there will need to an entire subsystem to manage clients, and ideally provide a web application or other way for users to sign up and have accounts. That's going to be a lot of work, and it's the kind of work I'm not currently interested in.

    Maybe by then someone else will want to contribute that to Obnam?

  • The HTTP API will be like this:

    • PUT /chunk?id=ID&label=LABEL
      • create a new chunk, unless a chunk with that ID already exists
      • both id and label are required
      • body is the blob with chunk content
      • requires an obnam_create claim
    • GET /chunk/<ID>
      • fetch a chunk by id
      • response body is the blob with chunk content
      • requires an obnam_get claim
    • GET /chunk?label=LABEL
      • find chunks with a given label
      • response is a JSON list of chunk identifiers
      • requires an obnam_find claim
    • DELETE /chunk/<ID>
      • remove a chunk
      • requires an obnam_delete claim
  • The usual HTTP response codes are used, as defined in https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status. There is also https://http.cat/ but I find the textual one more useful.

  • I will test the server using curl, but will also change the Obnam client to provide commands for this. Something like obnam api create --id=ID --label=LABEL FILENAME, but I don't know if I'll have time for that today. It'll be necessary for having a test suite for the server, though.

Notes

  • First step is to set up scaffolding for the server program. This is straightforward, I can just model it on the client. At first I will only implement the version command.

  • I'm going to have to figure out what to use for logging in the server. The env_logger is fine for many programs, but the server will be multi-threaded and probably tracing will work better. Might want to use that for the client too, for that matter. But I'll start with env_logger for now.

  • I added the scaffolding, with a version subcommand. As a user I like having both a --version option and a version subcommand. I like git-testament for more precise version numbers.

  • Next, a server configuration file. Apparently pasetors doesn't support serde for the key types it has so I'll need to handle that myself. A little bit of glue code is fine. I'll store the public and secret keys as byte vectors. I'll also use clingwrap to read the configuration file.

  • Setting this scaffolding up for every new program is not as much work as it used to be, before I wrote clingwrap, but it's still more than I'd like. I'll need to think about how to streamline this more. But not today.

  • One thing I notice right now is that if I want to keep server stuff separate from client stuff, and I do, it gets tricky to put everything into src/bin/obnam-server.rs, as that grows pretty large. I should split earlier the obnam crate into a workspace with crates for the client, the server, and shared code. But maybe not right now.

  • My next step will be to add the key pair for token signing key pair to the server configuration file. I made a prototype repository for experimenting with pasetors, and it looks like I can I used that code almost as-is. However, this becomes a bit messy if I don't split the obnam crate into a workspace, so I'll actually do that first.

  • I'll have a workspace with members. I've not used workspaces in my own projects before, so I get to learn something. In the Subplot project the workspace has a root package. In the Radicle heartwood project we use a workspace without a root package.

    It doesn't seem to really matter which approach one uses. Intuitively the lack of a root package seems tidier to me, so I'll go with that.

  • I'll have crates obnam and obnam-server. If they share code, I'll add obnamlib or something. I don't want to put crates in a crates directory as there's at most three of them. I start by adding the obnam crate, then extract what I did for the server into obnam-server.

  • The client and server will have separate version numbers. I specifically do not want them to match. It is an important goal for Obnam that they do not need to match.

  • As long as I have a single program for each crate, I can just put it main.rs I guess. I'll do that for the server now, and maybe change the client later.

  • OK, that was fun. Now I'll refactor the server code so that there's a library. Moved the server config there.

  • Next, add a module for access tokens based on the code I prototyped last time.

  • Added token signing key pair to server config.

  • Next I'll need to add commands to the server to generate a key pair and to create a token. But I ran out of time for today, so I'll continue from here next time.

Comments?

If you have feedback on this, please use the following fediverse thread: https://toot.liw.fi/@liw/115994612673569524.