Troubleshooting Sequoia with Zola

2026-08-24

I use Sequoia to publish my Zola site to AT Protocol. My setup is Sequoia 0.5.7 on Windows, Zola as the static site generator, and a self-hosted PDS.

Most of the workflow is straightforward, but I ran into a few issues where the documented workflow did not quite match how my setup behaved. Since these problems took some time to trace down, I am documenting the findings here in case they are useful to anyone running a similar setup.

CI/CD authentication and self-hosted PDSes

My first problem was with Spindles CI/CD. I configured app-password authentication using:

ATP_IDENTIFIER
ATP_APP_PASSWORD

The documentation indicates that PDS_URL is optional and can be resolved automatically from the identifier. I therefore expected Sequoia to discover my self-hosted PDS.

Instead, authentication repeatedly attempted to connect to https://bsky.social.

The fix was simply to provide PDS_URL explicitly:

ATP_IDENTIFIER=...
ATP_APP_PASSWORD=...
PDS_URL=https://my-pds.example

In Sequoia 0.5.7, the app-password authentication path did not resolve my PDS automatically from the identifier in this CI/CD setup. It also did not pick up pdsUrl from sequoia.json.

This was particularly easy to miss because the configuration looked valid according to the documented workflow. If you use a self-hosted PDS with CI/CD, explicitly setting PDS_URL is therefore worth doing.

Why cosmetic edits can create a new record

I also ran into duplicate records when making seemingly insignificant changes to Markdown.

For example, Sequoia may inject additional line breaks into a document. If I later clean those up manually, the rendered content is unchanged, but the Markdown source is different.

Sequoia's contentHash is based on the Markdown source, so the hash changes in this situation.

The workaround I found is:

real edit      → publish
cosmetic edit  → sync → publish

sync updates the local content hash, so publishing afterward recognizes the current file state instead of treating it as an unaccounted-for change.

However, this was only part of the duplicate-record problem.

Zola frontmatter and atUri

The more important issue was how Sequoia discovers the existing AT Protocol record.

I initially put the document's atUri into Zola's [extra] frontmatter so that I could access it directly from my templates. This follows the documented workflow for manually controlling atUri injection.

For example:

[extra]
atUri = "at://did:plc:r46eildr6uztgnr6bbwcnhrh/site.standard.document/3mtu5fhs4bk2v"

With Zola, this did not work as expected for Sequoia's record detection.

The important distinction is that Sequoia could still write the information into the frontmatter, but it could not reliably read the value back from Zola's [extra] structure when looking for the existing record.

That creates a particularly confusing failure mode:

  1. I modify the article.
  2. The contentHash changes.
  3. Sequoia needs to find the existing atUri.
  4. It cannot find it through the Zola [extra] field.
  5. It publishes a new record with a new rkey instead of updating the existing one.

So a changed hash by itself does not necessarily mean that Sequoia should create a new record. The existing record also needs to be discoverable.

For now, I avoid relying on [extra] for Sequoia's record discovery and treat its synchronization state as the source of truth.

sequoia inject and Zola's index.html

There is one more Zola-specific issue I encountered with sequoia inject.

Zola uses directory-based URLs, with pages commonly represented as:

post/
└── index.html

If an article's slug happens to overlap with a directory in the generated site, sequoia inject can inject the document URI into the wrong index.html.

The command can complete successfully, so this is easy to overlook. I now check the generated output after injection when working with potentially ambiguous paths.

My current workflow

With these issues in mind, my workflow is roughly:

The main thing I learned is that several seemingly unrelated problems come from the interaction between Sequoia's document state and the conventions of the static-site generator. Once those boundaries are understood, the behavior becomes much easier to troubleshoot.

I have also opened issues upstream for the problems described here, so the findings and workarounds are documented alongside the project itself.

I am grateful that projects like Sequoia make this kind of AT Protocol publishing possible with free and open tooling. Hopefully documenting these edge cases saves some time for the next person combining Sequoia with Zola, a self-hosted PDS, or a similar setup.