From Idea to NuGet: My Journey to Publishing a .NET Open Source Library
When I was a kid, I wanted to be two things: an architect and an author. Not the kind of architect who manages cloud infrastructure or the kind of author who writes technology books — but the classic versions of these professions. I drew buildings on graph paper and devoured Roald Dahl and Michael Crichton novels. I was the fourth grader lugging around Jurassic Park, wearing glasses too big for my face, and dreaming big.
Fast forward to adulthood: I didn’t become an architect who designs skyscrapers, nor have I penned the next Andromeda Strain. But I did become an Architect…just a Software Architect, and I have published a book…just about Multi-Cloud Architecture and Infrastructure-as-Code. So maybe I didn’t completely fail after all — just took a slightly different path.
Recently, I crossed off another line from my list of childhood ambitions: I became a published NuGet package author.
Why Blue Sky? Why Now?
Most folks know me for my Terraform content or Azure architecture work. But this time, I stepped outside the infrastructure world to explore something that’s part social experiment, part technical curiosity: BlueSky, a distributed, democratic take on social media built on the AT Protocol.
Blue Sky offers something rare — an open protocol (Authenticated Transfer Protocol, to be specific) that allows anyone to build tools, clients, and integrations. And yet, when I looked around, I couldn’t find a .NET SDK for it. That was my window.
No SDK? Great! I’ll write one. And I’ll finally publish a NuGet package in the process.
The Architecture Behind the Butterfly
Under the hood, Blue Sky is just one implementation of the AT Protocol stack, composed of three parts:
- DID (Decentralized Identity): For identity resolution.
- PDS (Personal Data Store): Where user data is housed.
- Aggregator: A feed aggregator that pulls data from multiple PDS sources.
When you sign up for Blue Sky, you’re using their PDS, aggregator, and DID provider. But the magic is that you don’t have to. You can self-host these components and connect into the network. It’s as close to decentralized as you can get without diving headfirst into the blockchain world.
Building the SDK: Ten Easy(ish) Steps
Here’s how I turned that kernel of an idea into a living, downloadable NuGet package.
1. Set Up the GitHub Repo
This part was easy. I’ve been around GitHub long enough to have muscle memory. Standard layout: /src, /docs, and .github/workflows for GitHub Actions. I like keeping things tidy.
2. Pick a License
For no particularly strong reason, I went with MPL 2.0. GitHub makes it easy, and it sounded safe. Don’t overthink this part — just don’t leave it blank.
3. Write the Code
Without a Swagger spec or OpenAPI definition for Blue Sky, I had to hand-roll all my HTTP calls. That actually made the SDK more focused. I started with just enough functionality to post simple text updates.
And then something magical happened: people I didn’t know started submitting PRs. One contributor, Johan, added image posting. Carl Franklin (yes, that Carl Franklin of .NET Rocks!) submitted a follower retrieval feature. Collaboration! Real open source energy.
4. Write the README (or Don’t)
I hate writing README files. Enter ChatGPT. I fed it some code, asked for a “README for dumb users,” and it gave me a surprisingly coherent structure. I made a few manual edits and called it good. You should still read what AI generates, but it saves a ton of time.
5. Testing the SDK (In Production, of Course)
Integration testing was the tricky part. There’s no sandbox or test environment for Blue Sky. I ended up creating a test Blue Sky account, wiring up GitHub Actions to inject secrets (username, password), and running the tests against the real live platform.
# Step 4: Run tests
- name: Run Unit Tests
env:
BLUESKY_HANDLE: $
BLUESKY_PASSWORD: $
run: dotnet test src/Qonq.BlueSky.Tests/Qonq.BlueSky.Tests.csproj --logger "trx;LogFileName=TestResults.trx"
Yes, I’m testing in production. And yes, I’ve made peace with that.
6. Add Secrets to GitHub
Credentials, API keys, all the fun stuff. Secrets are safely injected into the GitHub Action workflows to keep the tests humming along without exposing sensitive data.
7. Run Integration Tests
To ensure the SDK remains reliable as it evolves, I set up GitHub Actions to automatically run the integration test suite on every pull request and merge to the main branch. This helps catch regressions early, especially as contributors submit changes or I refactor features.
Since the library interacts with a live BlueSky account, the tests are wired to authenticate using secrets stored securely in GitHub, allowing each test run to validate real-world functionality — like posting content, uploading images, and following users. It’s a lightweight suite for now, but it gives me confidence that any update won’t silently break core features.
I started small with a single capability of posting a text message.
Beep, Beep Boop!
Yes, we are all just bots anyway, right?
Then some friendly internet strangers started popping up out of nowhere dropping new functionality on me! How exciting!
Testing with images? Johan added support for that. Originally, he used a blank white image, but that didn’t sit right with me. So I jazzed it up with memes. The tests now post actual meme images, captioned to reflect our testing-in-production antics. You’re welcome, internet.
To test follow/unfollow functionality, I picked a famous Blue Sky user: Kevin Bacon. The six degrees of separation joke basically wrote itself. Unfortunately, I may have followed/unfollowed him so often that he rage-quit the platform. Sorry, Kevin.
I now test against the official Blue Sky account. They seem less emotionally fragile. Probably more unlikely to rage quit BlueSky.
8. Sign up for NuGet
Before I could publish the package, I had to dust off my old NuGet account — or so I thought. I’d originally signed up over a decade ago, but when I tried logging in, the account had seemingly vanished into the void. Whether it was due to inactivity or a long-forgotten angry email, I’ll never know.
In the end, I created a brand-new account using a different email and set up a fresh organization to host the package. I was surprised by how polished the NuGet publishing experience has become — it felt more like an app store than a barebones package host. Once my new credentials were sorted, I generated an API key, dropped it into GitHub Secrets, and I was finally ready to ship my first public package.
9. Publish to NuGet
I didn’t want to use PowerShell for packaging (I really dislike PowerShell), so I scripted everything in Bash. My publish workflow now builds, versions (using date and build number), and pushes the package automatically.
Current download count? 102. Probably 98 are me. But hey, triple digits!
10. GitHub Action to Publish Nuget
To automate publishing the NuGet package, I added a GitHub Action that packs and pushes the library to nuget.org. The action dynamically generates a semantic version number based on the current date and GitHub run number (e.g., 2025.03.27.42), ensuring each build is uniquely versioned without manual intervention.
- name: Pack NuGet package
env:
NUGET_API_KEY: $
run: |
echo "Generating build number..."
BUILD_NUMBER=$(date +%Y.%m.%d).$
echo "Build number: $BUILD_NUMBER"
dotnet pack src/Qonq.BlueSky/Qonq.BlueSky.csproj --configuration Release --no-build -o ./nupkgs /property:PackageVersion=$BUILD_NUMBER
dotnet nuget push --source 'https://nuget.org' --api-key $NUGET_API_KEY nupkgs/*.nupkg
It uses dotnet pack to create the .nupkg file and then pushes it using dotnet nuget push. The API key is securely stored in GitHub Secrets and passed in as an environment variable. This lightweight script, written entirely in Bash (no PowerShell!), keeps the process clean and CI-friendly—just trigger the workflow and your package is live.
Lessons Learned
Let’s be real — Blue Sky doesn’t offer a non-production API. If you’re building something like this, you have to test in prod unless you want to self-host the entire stack. Which sounds miserable.
So I embrace it. I post memes, delete them, follow people, unfollow them. My integration tests are my live account. It’s fine. I have a whole suite of tests, including multi-image posts, session validation, and more.
The hardest part? Initially, was understanding DIDs and the session mechanics. Authentication in Blue Sky isn’t your standard OAuth flow; it’s a lighter, simpler ROPC-like flow. Once I wrapped my head around that, the rest came together.
Also: don’t underestimate how fun it is to receive PRs from strangers. Or how hard it is to write code you know others might read. But it’s rewarding.
What’s Next?
There’s still more to build. Right now, I can post content, but I can’t fetch post histories. There are more record types in the AT Protocol I haven’t implemented. My issue list is open — pull requests are welcome.
Want to Help?
- Implement post retrieval (for a given user account)
- Add more record operations (AT-protocol stuff)
- Improve documentation (yes, even snarky READMEs are welcome)
Go submit a PR!
Conclusion
So no, I didn’t become the kind of architect I envisioned as a kid. And I haven’t written a best-selling novel. But I did create something real, useful, and collaborative. And it exists on the open internet.
I became an architect of a different kind, and an author in a different language — Terraform, C#, YAML. And now, thanks to Blue Sky and NuGet, my boyhood dreams are at least partially fulfilled. With memes.
Check out the package. Fork the repo. And remember: testing in production isn’t always the worst idea — as long as you’ve got good memes to go with it.