Have your say on the future of fastly.toml

Since we launched Compute (then known as Compute@Edge) in 2019, developers have made use of the fastly.toml file format to describe the properties and requirements of their edge services.

The format, represented in TOML (Tom’s Obvious Minimal Language), has served our developer community well for those almost five years, but as the platform has grown and the way that we want to build Compute services has become clearer, it’s apparent that it is time to provide an easier way to specify the needs of your edge packages.

That’s why I’m pleased to kick off the next iteration of the Compute package manifest format, v4, by reaching out to you, Fastly’s developer community, to find out what you would like to see going forward.

The big question

Defining resources

Compute packages are more than just code, they depend on a variety of external resources – backends, config stores, secret stores, and the rest. One of the biggest challenges we’ve seen developers face is understanding the difference between locally configured resources (in fastly.toml’s [setup] and [local_server] sections) and remote resources configured on the Fastly control panel, CLI, or API.

Let’s start with some context – the [setup] section was designed to provide hints to our developer tooling about what resources are required by a service (but not their values, more on that later), to allow it to prompt developers to create them on the initial deployment of a starter kit. It does that well, but has led to some confusion around future deployments and whether updating values in the [setup] section would cause them to be updated Terraform-style on the remote service. Spoiler: it does not.

Then came Viceroy, our local testing server, which requires more than just knowing which resources are required but also needs their values. It’s not helpful for the server to know that a backend named web_assets is required, it also needs to know where to find it. This led to the introduction of the [local_server] section, which provides a similar (but unfortunately different!) format for defining resources as well as their configuration values.

These have existed in parallel since 2021, and you (our developer community) have made it work for your projects. But it’s clear that there must be a better way to specify this information in one place, rather than twice for each project. How do you imagine this would look and function going forward?

A smaller quality-of-life improvement

The verbosity of TOML

TOML is a fantastic language, and really shines in situations like the Cargo manifest format. However, the complex nature of Fastly service configurations can lead to something that is not very friendly to write by hand:

[setup]

  [setup.backends]
  
    [setup.backends.httpbin]
    
      address = "httpbin.org"
      
      description = "A simple HTTP Request & Response Service."
      
      port = 443

    [setup.backends.httpme]
    
      address = "http-me.glitch.me"
      
      description = "HTTP me is a tiny express app initally designed to replicate the features of HTTPBin.org"
  
      port = 443

[setup.config_stores]

  [setup.config_stores.service_config]
  
    description = "Configuration data for my service"
  
    [setup.config_stores.service_config.items]
    
      [setup.config_stores.service_config.items.s3-primary-host]
      
        value = "eu-west-2"
      
      [setup.config_stores.service_config.items.s3-fallback-host]
      
        value = "us-west-1"

As a large majority of new developers on Compute are using JavaScript, I wonder if it would be beneficial to switch to a more commonly-known format, such as JSON (à la npm’s package.json) or perhaps its lesser-known but more flexible successor, JSON5, which would retain TOML’s ability to include comments in the file. How do you feel about this? Is there a format that you think would be more suitable?

Anything else?

We want to hear your honest feedback about developing on Compute! This thread is open for your input on the above questions and any other thoughts you may have about the Compute package manifest format. We’ll be taking your discussion seriously and using it to inform the next version.

Also, stay tuned for a special episode of Fastly Developers Live next week where we’ll be bringing together some of the key engineers at Fastly working on Compute and developer tooling to have a chat about the above questions, as well as review your input from this thread. Click “Notify me” on the stream page to be notified when we go live: https://youtube.com/live/WjlRMZQ1CiY

3 Likes

I’d love to make it easier to spin up Fastly services from Glitch. Currently we write the TOML on the fly when someone remixes a project so that we can set their Glitch website address as the origin… It would be a lot easier to integrate into the Glitch editor flow if the file was in JSON so for my own selfish reasons I’d vote for that lol. (I wonder if there are other cases where it would help people integrate Fastly into their development flow by defining services in the context of an origin application rather than as a separate component…!?)

1 Like

It’s also worth mentioning that the Fastly API accepts JSON, so the current TOML configuration ultimately gets converted to JSON anyway whenever the CLI uses it to create resources.

By having resources in JSON we could match the data structures that the API expects, improving consistency across tools.

1 Like

:wave:

I’m very much in the “move to JSON” camp here :slight_smile:

NOTE: It’s encouraging to hear that the TOML is actually converted into JSON when publishing to the Fastly platform! I didn’t realise that :sweat_smile:

I know from experience developing the Fastly CLI that the TOML support is poor in the Go community and our need to make edits dynamically is very hard to achieve.

This has also resulted in confusion for customers who like to write their own TOML (as TOML can be written in a very concise syntax). Whenever the CLI has to process the TOML file and then make edits to it, we have to construct the TOML into an AST and this then gets persisted back to disk in its verbose form. Thus causing confusion because people don’t expect their TOML to change in that way (and visually it’s pretty nasty too :slight_smile: ).

2 Likes

Yes! I have to admit that this can be quite frustrating as I like to structure the fastly.toml in a specific way.

I guess the wider point here is that JSON support is generally better, if not native, in most programming languages. At least more so than TOML.

@triblondon reached out for my thoughts on this, bit scrappy but here they are:

We don’t actually use much if any compute at the FT still, so this is just my thoughts rather than leaning on wider experience.

Off the top of my head I’d be looking at other hosting platforms and what conventions might exist already. I’m aware of:

At the FT I think we’re not big adopters of Terraform yet, so something like a IaC config file has value to help engineers not as confident in the wider IaC skillset. The DigitalOcean config file does well at this, it will update resources after setup if they are changed from what I remember and I think that’s helpful.

I’m a +1 on the whole local vs. remote backend config, simplifying that seems helpful as that whole area is a bit unintuitive for engineers that haven’t previously worked with a VCL service and understand the backend concept. There’s a curious thing here between compute maybe wanting to be a genetic hosting platform? but still being a reverse proxy fundamentally.

TOML vs. json, etc. :person_shrugging: I would focus more on it not being full of leaky abstractions, though I can appreciate that has to be hard at a multi-client platform level while still making room for future stuff. From the example it doesn’t seem you’d get much from TOML, seems better suited to config that isn’t as deeply nested. Though that example as-is maybe undersells it given you can use complex types in the values right?

1 Like

Thanks for the links, I’ll definitely take a look at these.

I think this way of thinking comes from the fact that Fastly has always fundamentally been a reverse proxy, but the truth is that Compute is a generic compute platform, especially for newer customers that are not familiar with the Deliver product and don’t have that preconception. There is no requirement that you forward requests to a backend, or even have backends defined at all.

This is a fair point, and the main priority here is definitely improving the schema itself. Switching to JSON (or another language) at the same time is just something we can do if there is significant interest.

Some ideas from my relatively short time using Compute, and helping others use Compute:

  • Some way to declare the resources that are needed without any definition of their values ('this code needs a KV store name ‘abc’ and a Config store named ‘xyz’ and two static backends named ‘bk1’ and ‘bk2’)
  • Rename ‘local_server’ to ‘test’ or something else; nobody really expects to run a Compute service locally (with actual traffic handling), instead they use it for testing the service code’s behavior, so the naming of the data used for testing should reflect that
  • Absorb the ‘multiple environment’ support into a single file, with some sort of inheritance for common content between environments
1 Like

As it turns out that’s a conceptual misunderstanding which occasionally causes problems for people writing Compute services (including Fastlyans!). While there are some aspects of ‘reverse proxy’ behavior if your service code is constructed that way, it’s not mandatory, and in fact the behavior of our default starter kits is decidedly not reverse proxy, as they don’t ever see the request or response bodies. Since that is the case, when a Compute user tries to do something in their code which requires access to one (or both) bodies, they find that the behavior changes in surprising ways.

I’d definitely like to see the package manifest reflect this distinction where it can do so, to help users avoid this situation.

There is something I would like to see added to the “setup” section: products enablement.

Currently there is a set of “products” that can be enabled by using the fastly cli:

% fastly products
PRODUCT            ENABLED
Brotli Compression  false
Domain Inspector    false
Fanout              true
Image Optimizer     false
Origin Inspector    false
Web Sockets         false

Fastly Applications (including starter kits) would benefit from being able to declare that the app requires product X. The syntax can be something simple like this (but open to discussion):

[setup.products]
fanout = true

(At present, the valid keys are: brotli_compression, domain_inspector, fanout, image_optimizer, origin_inspector, websockets)

At the current moment, publishing an app that requires such a product enablement means to publish to production (fastly compute publish), but that app would be unusable until returning to the command prompt and enabling the product in a separate call (fastly products --enable=fanout).

2 Likes

Since you’ve asked for perspectives :), here’s ours: We currently don’t use fastly.toml almost at all.

For production, since fastly compute deploy only creates resources like backends/domains/logging endpoints/etc, and does not update them if their configuration changes, we have to use terraform to control these aspects anyway. I’d love if we didn’t have to do that, but I guess that’s a completely different topic.

For local/testing, since we already need to enrich backend requests with additional testing headers and the like, to support introspections/assertions in our test harness, we also map all backends to a single testing (httpbin-style) backend in our application code, while we’re at it anyway. Thus the file for viceroy literally is

[local_server.backends.testing]
url = "http://localhost:9051"

To be fair, we currently don’t use any Compute features like config stores, so that of course simplifies matters here.

3 Likes

It’s actually part of this topic (see the reference to “IaC” above). There are many other users who also wish to be able to maintain their Compute services using the package manifest if they don’t need the full power of Terraform.

Thanks everyone for your feedback so far!

I’d love to have you join me live on YouTube later today, where I’ll be chatting with members of the Developer Experience team as we review and discuss all of your input:

YouTube link

3 Likes

I definitely understand the downsides of TOML, and JSON is a good interchange format, but I don’t think of JSON as being a particularly good configuration language. The lack of first-class comments especially is a major knock against it for this purpose, IMO.

I don’t have a great counter-recommendation, though. YAML has its own set of problems, and HCL doesn’t seem to have a lot of support outside of Hashicorp’s tools. INI files are probably too freeform for what we’d like.

1 Like

re: JSON comment…

The lack of first-class comments especially is a major knock against it for this purpose, IMO.

Agreed.

But if we use https://json5.org/ then that wouldn’t be an issue.

1 Like

I suspect this is out of scope of this topic (sorry in advance if it was), but in addition to improvements in file format and contents definitions, one thing I’d like to see in the future is an additional capability on how fastly.toml is handleded by CLI.

Currently I get an error with the commands below;

% mkdir /tmp/mytest && 
  cd /tmp/mytest &&
  cargo init &&
  echo 'fn main(){println!("hello");}' > main.rs &&
  fastly compute build --package-name=test --language=rust
✗ Verifying fastly.toml

ERROR: error reading fastly.toml.

I can fix this by %touch fastly.toml though, I wonder if I could build or run %fastly compute serve without the need to create fastly.toml.

For the similar reason, I’d also love to see the requirements for deployment to the production fleet. The other day I learned that no contents is actually required to deploy a package [1] at the moment. If this is intentional(I hope so!), maybe technically fastly.toml-less deployment could be also doable.

As an occasional minimalist, I’d love to see the additional capability such that fastly.toml could be handled optionally too depending on user preference for better developer experience (or if it’s not optional, maybe giving some better guide info in CLI by showing template fastly.toml contents upon encontering a build/deploy error).

[1] smol/scripts/build at main · JakeChampion/smol · GitHub

Yeah, although I think that neutralizes one of the strongest arguments for JSON, which is that its tooling and library support is pretty great everywhere. From an ecosystem perspective, is JSON5 better than TOML? (or YAML, or…)

At Climatiq we use fastly.toml reasonably extensively - both for manual testing, and local e2e testing. So we have multiple files, with different backends, and dynamically populated values for KVStore and Dictionary entries. I have a few thoughts

File Format

Regarding the format I don’t have strong preferences. I do not think straight JSON is a good choice. I do not think a language without comments is a good choice for a configuration language. JSON5 is fine, but at that point I don’t see many advantages for it over TOML, as you’ll need to pull in third party libraries to parse it in most cases. Personally I’m not convinced the upsides is worth the churn, but I also don’t have super strong preferences.

Renaming of local_server

Rename ‘local_server’ to ‘test’ or something else; nobody really expects to run a Compute service locally (with actual traffic handling), instead they use it for testing the service code’s behavior, so the naming of the data used for testing should reflect that

I don’t see this as a good change. Especially as Viceroy can be used in Rust both as a test runner and a local server, I think the test name is confusing. To me local_server is a good name - it says exactly what it does on the tin - it runs a local server.

Multiple environments in one file

It might be nice to have a better handling of environments than currently. At the moment to change e.g. a backend for our integration environment, I’ll need to copy the entire fastly.toml file, and keep both in sync. Perhaps an environment-specific section inside the file could help alleviate this.

Allowing you to specify states and values in different places

This is my biggest issue. Currently we use dictionaries and KVStore defined in our fastly.toml. Dictionaries are significantly easier to use because we are able to define the values in a separate JSON file and share the values across environments and not check it into version control.

Contrast this workflow for writing secrets/keys/etc and storing them in a dictionary across multiple envs:

  • Generate a gitignored values.json file via a script
  • Refer to it in both fastly.toml files

Now because KVStores don’t have this capability, what we need to do is:

  • Generate a fastly.toml.template and a fastly.integration.toml.template
  • Have a script that generates the secrets (that are different per machine). This script now needs to:
    • Generate the secrets (same as before)
    • Parse both TOML files
    • Insert the secrets inline into the TOML files
    • Copy the result into two new TOML files that are gitignored.

This is significantly less ergonomic.

6 Likes

A feature request:

When you’re setting up your declarations for resources like KV/config/secret store, it would be nice to have a way to link to existing resources via their resource id.

I sometimes have KV Stores with hundreds of entries that aren’t practical to specify inside fastly.toml.

To clarify, do you mean that you’d like the local testing server to use the data defined in a real KV store/config store/whatever on the Fastly platform? That sounds helpful.