= Tangled Services: Git Hosting with ATProto Identity
== The Convergence of Git and Social Identity
Tangled represents a novel approach to code hosting that merges traditional version control with ATProtocol's decentralized identity system. Rather than using usernames and passwords or SSH keys for authentication, Tangled uses DIDs (Decentralized Identifiers) to establish identity and authorization.
This architecture means your identity on Tangled is the same as your identity on Bluesky or any other ATProtocol service. Your repositories are linked to your DID, and access control is managed through cryptographically verifiable records rather than traditional permission systems.
== Tangled Knot: The Git Server
Knot is the git hosting component. Conceptually, it's similar to GitHub or GitLab, but with fundamental differences in how identity and authorization work.
=== Identity-Based Authentication
When you push code to a Tangled repository, the authentication flow works like this:
1. Your git client presents your DID (from your ATProto account)
2. Knot looks up this DID in the ATProto network
3. It verifies your identity by checking cryptographic signatures
4. It checks authorization records—ATProto records that specify who can access which repositories
5. If authorized, the push proceeds
Why this matters: Your "login" to Tangled isn't a separate account—it's your existing ATProto identity. If you've already verified your identity for Bluesky, you're already verified for Tangled. There's no separate password to manage or SSH key to configure.
=== Repository as Social Object
In traditional git hosting, a repository is just a place to store code. In Tangled, a repository is a social object that participates in the ATProto network:
This makes code a first-class citizen of the social web. When you star a repository or contribute to it, those actions can be visible in your social feed alongside posts and likes.
=== The Server Configuration
Knot needs to know:
Hostname: The public domain where Knot is accessible. This is used for URL generation (clone URLs, web links) and for ATProto record addressing.
Owner DID: The DID of the entity operating this Knot instance. This is used for service identification—when Knot publishes records, it signs them as this DID.
Data Directory: Where git repositories are stored on disk. This needs to be persistent and have sufficient space.
Endpoints: Knot needs to know how to reach other ATProto services—the AppView, the PLC directory, etc.
=== Git Protocol Support
Knot supports the git protocol over HTTP. When you clone or push, you're making HTTP requests that Knot handles. This is why Caddy can proxy to Knot—git-over-HTTP is just HTTP with special headers and request formats.
The openFirewall option in the NixOS module opens the port that Knot listens on (default 5555). However, since you're using Caddy as a reverse proxy, external users don't connect directly to this port—they connect through Caddy, which forwards to Knot.
== Tangled Spindle: CI/CD Event Processing
Spindle is the continuous integration and deployment component of Tangled. It listens to ATProto events and triggers builds when appropriate.
=== Event-Driven Architecture
Spindle connects to Jetstream (the ATProto event firehose) and watches for specific types of events:
When it sees an event that requires action, it:
1. Pulls the repository from Knot
2. Reads the build configuration from the repository
3. Runs the build in a container
4. Reports status back to ATProto
=== The Job Queue
Builds don't happen immediately— they're queued for processing. This provides several benefits:
Resource management: You can limit concurrent builds to prevent overwhelming the server.
Fairness: Jobs are processed in order, preventing one user from monopolizing resources.
Reliability: If a build fails, the queue system can retry or alert appropriately.
Your configuration sets maxJobCount = 4 (four concurrent builds) and queueSize = 100 (queue up to 100 jobs). If more than 100 jobs are pending, new ones are rejected.
=== Build Isolation with Containers
Spindle runs builds in containers for isolation and security. Your code can't affect other users' builds or the host system.
The NixOS module configures which container runtime to use and how to fetch container images. By default, it uses Nixery (a service that generates container images from Nix expressions) to get build environments.
=== Secrets Management
CI builds often need secrets—database passwords, API keys, deployment credentials. Spindle supports multiple secret backends:
SQLite (simple): Secrets are stored in a local SQLite database. This is easy to set up but less secure for production.
OpenBao (production): OpenBao is an open-source secrets manager (fork of Vault). It provides dynamic secrets, access control, and audit logging.
Your configuration uses SQLite, which is appropriate for a personal or small-scale deployment.
=== The Relationship Between Knot and Spindle
Knot and Spindle are separate services that communicate through the ATProto network:
1. You push code to Knot
2. Knot publishes a record announcing the push
3. Spindle sees this record via Jetstream
4. Spindle pulls the code from Knot
5. Spindle runs the build
6. Spindle publishes a record with the build result
This is a loosely coupled architecture. Knot doesn't know about Spindle, and Spindle doesn't know about Knot except as one source of repositories. This means:
=== Endpoints Configuration
Spindle needs to know how to reach various services. These are configured as endpoints:
AppView: Where to publish build results so they appear in clients
Knot: Where to fetch repositories from
Jetstream: Where to subscribe for events
Nixery: Where to get container images
ATProto/PLC: Core protocol services
These URLs let Spindle participate in the broader ATProto ecosystem. If you were running your own AppView or Knot instances, you'd point Spindle to those instead of the public ones.
== Workflow Configuration
Spindle reads build configuration from a file in your repository (similar to .github/workflows or .gitlab-ci.yml). The format specifies:
When Spindle processes a push event, it looks for this configuration file. If found, it creates a job based on that configuration. If not found, it does nothing (not all repositories need CI).
=== Timeouts and Limits
Your configuration sets workflowTimeout = "10m" meaning each build step has 10 minutes to complete. This prevents runaway builds from consuming resources indefinitely.
You can also set resource limits (CPU, memory) per build. This prevents one user's build from affecting others.
== Social Integration
Because Tangled is built on ATProto, it integrates with the social web in ways traditional git hosting doesn't:
Activity feeds: When you push code, star a repository, or a build completes, these can appear in Bluesky feeds or other ATProto clients.
Social discovery: You can find repositories through your social graph—repositories your friends have contributed to, starred, or created.
Unified identity: Your reputation and identity in the code world is the same as your identity in the social world. No need to establish credibility separately.
Notifications: Build failures, new issues, and other events can generate ATProto notifications that appear in your client of choice.
== Data Storage
Knot stores:
/var/lib/tangled-knot)Spindle stores:
Both need persistent storage. If you lose this data:
== Scaling Considerations
Storage: Git repositories can grow large. Monitor disk usage and implement cleanup policies for old build artifacts.
Concurrent builds: The maxJobCount setting limits parallelism. Increase this if you have CPU capacity and need more throughput.
Queue depth: If builds are consistently queuing up (queue is often near full), you need more capacity—either faster builds or more concurrent jobs.
Network bandwidth: Pulling repositories and pushing artifacts consumes bandwidth. For large repositories or frequent builds, this can become a bottleneck.
== Security Model
Authentication: Via ATProto DIDs and cryptographic signatures. No passwords to steal or brute force.
Authorization: Through ATProto records that specify access rights. These are signed and verifiable.
Isolation: Builds run in containers with limited resources and no access to the host system or other builds.
Secrets: Stored encrypted and only available to authorized builds. With OpenBao, secrets can be dynamic (generated on demand, time-limited).
== Monitoring and Debugging
Knot logs: Show authentication attempts, repository access, push/pull operations
Spindle logs: Show job queue activity, build progress, errors
Metrics: Both services can expose Prometheus metrics for monitoring
Common issues:
== Integration with NixOS
The NixOS modules for Knot and Spindle handle:
The services run as dedicated users (tangled-knot, tangled-spindle) with limited privileges. They can only access their data directories and the network.
When you change configuration and rebuild, NixOS updates the services. For most configuration changes, this just reloads settings. For some changes (like port modifications), the services restart.
== Future Possibilities
The modular, event-driven architecture of Tangled enables many possibilities:
Multiple executors: Run builds on different machines or cloud providers based on load or requirements.
Custom build environments: Use Nix to define reproducible build environments instead of containers.
Integration with other services: Webhooks, notifications, deployment to various platforms.
Federated hosting: Multiple Knot instances that federate, allowing users to choose where to host while maintaining a unified social experience.
Tangled represents a new paradigm for code hosting—one that treats code as part of the social web rather than separate from it. Understanding this architecture helps you appreciate why certain configuration choices matter and how the pieces fit together.