Skip to main content
View all authors

Build and Publish Serverless Functions with GitHub Actions

Β· 7 min read


One of the principles that shaped Juno from day one was the idea of building apps with full ownership β€” no hidden infrastructure, no opaque servers.

No hypocrisy either.

If developers are encouraged to deploy code in containers they control, it feels inconsistent to rely on centralized infrastructure β€” like AWS or other Web2 cloud providers β€” to manage deployment pipelines or run the platform. With the exception of email notifications, Juno currently runs entirely on the Internet Computer β€” and that's a deliberate choice.

That doesn't mean being stubborn for the sake of it. It just means trying to push things forward without falling back on the old way unless absolutely necessary.

At the same time, developer experience matters β€” a lot. It shouldn't take a degree in DevOps to ship a backend function. Developers who would typically reach for a serverless option should be able to do so here too. And for those who prefer to stay local, it shouldn't feel like a downgrade β€” no one should be forced into CI automation if they don't want to.

That's why the new GitHub Actions for serverless functions are now generally available β€” for those who want automation, not obligation.


πŸš€ Automated Deployments, No Compromise​

With the latest release, it's now possible to:

  • Build serverless functions written in TypeScript or Rust
  • Automatically publish them to a Satellite
  • Optionally propose or directly apply upgrades

All within a GitHub Actions workflow. No manual builds, no extra setup β€” just code, commit, and push.

This makes it easier to fit Juno into an existing CI/CD pipeline or start a new one from scratch. The logic is bundled, metadata is embedded, and the container is ready to run.


πŸ” What About Security?​

You might ask yourself: "But what about the risk of giving CI full control over my infrastructure?"

That's where the improved access key (previously named "Controllers") roles come in.

Instead of handing over the master key, you give CI just enough access to do its job β€” and nothing more.

Here's how the roles break down in plain terms:

  • Administrator – Full control. Can deploy, upgrade, stop, or delete any module. Powerful, but risky for automation. Might be useful if you're spinning up test environments frequently.

  • Editor (Write) – Ideal for CI pipelines that deploy frontend assets or publish serverless functions. Can't upgrade those or stop and delete modules. A good default.

  • Submitter πŸ†• – The safest option. Can propose changes but not apply them. Someone still needs to review and approve in the Console or CLI. No surprises, no accidents.

Use Editor for most CI tasks β€” it gives you automation without opening the blast radius.

Prefer an extra layer of review? Go with Submitter and keep a human in the loop.


🧰 Local or CI: Your Choice​

Nothing changes in the approach for developers who prefer local development. The CLI remains a first-class tool for building and deploying.

All the new capabilities β€” from publishing functions to proposing or applying upgrades β€” are available not just in GitHub Actions or the Console UI, but also fully supported in the CLI.

In fact, the CLI has been improved with a neat addition: you can now append --mode development to interact with the emulator. This allows you to fully mimic production behavior while developing locally. And of course, you can also use any mode to target any environment.

juno functions upgrade --mode staging
juno deploy --mode development

πŸ›°οΈ Satellite's CDN​

While building serverless functions was never an issue, enabling GitHub Actions to publish and deploy without giving away full control introduced a challenge. How do you let CI push code without handing it the keys to everything?

That's where the idea of a sort of CDN came in.

Each Satellite now has a reserved collection called #_juno/releases. It's like a staging area where CI can submit new WASM containers or frontend assets. If the access key has enough privileges, the submission is deployed right away. If not, it's stored as a pending change β€” waiting for someone to approve it manually via the Console or CLI.

This builds on the change-based workflow that was added to the Console last year. Funny enough, it brought the Console so close to being a Satellite itself that it became… basically a meta example of what you can build with Juno.

And here's the cherry on top: because there's now a CDN tracking versions, developers can rollback or switch between different function versions more easily. A new CDN tab in the Console UI (under Functions) gives you access to all past versions and history.


πŸ–ΌοΈ Frontend Deployments, Too​

Frontend deployment now benefits from the same change-based workflow. By default, when you run juno deploy or trigger a GitHub Action, the assets are submitted as pending changes β€” and applied automatically (if the access key allows it).

Want to skip that workflow? You still can. The immediate deployment path remains available β€” handy if something fails, or if you just prefer to keep things simple.


πŸ› οΈ GitHub Actions for Serverless Functions​

Alright, enough chit-chat β€” here's how to publish your serverless functions on every push to main, straight from CI:

publish.yml
name: Publish Serverless Functions

on:
workflow_dispatch:
push:
branches: [main]

jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22
registry-url: "https://registry.npmjs.org"

- name: Install Dependencies
run: npm ci

- name: Build
uses: junobuild/juno-action@full
with:
args: functions build

- name: Publish
uses: junobuild/juno-action@full
with:
args: functions publish
env:
JUNO_TOKEN: ${{ secrets.JUNO_TOKEN }}

🌸 One Action, Two Flavors​

Noticed the @full in the previous step?

That's because the GitHub Action now comes in two flavors:

  • junobuild/juno-action or junobuild/juno-action@slim – perfect for common use cases like deploying frontend assets or running simpler CLI tasks. No serverless build dependencies included, so it's faster and more "lightweight" (relatively, it still uses Docker underneath...).

  • junobuild/juno-action@full – includes everything you need to build and publish serverless functions, with Rust and TypeScript support. It's heavier, but it does the job end to end.

The right tool for the right job. Pick what fits.


🧭 Where This Is Going​

This release isn't just about smoother deployments β€” it's a step toward making Juno feel like real infrastructure. Though, what is β€œreal infrastructure” anyway? Whatever it is, this one doesn't come with the usual baggage.

Developers get to choose how they ship β€” locally or through CI. They get to decide what gets deployed and who can do it. They're not forced to rely on some big tech platform for their infra if they don't want to. And thanks to the new CDN and access control model, fast iteration and tight control can finally go hand in hand.

If you've been waiting for a way to ship backend logic without giving up on decentralization β€” or if you just like things working smoothly β€” this one's for you.

Go ahead. Build it. Push it. Submit it. Ship it.

To infinity and beyond,
David


Stay connected with Juno by following us on X/Twitter.

Reach out on Discord or OpenChat for any questions.

⭐️⭐️⭐️ stars are also much appreciated: visit the GitHub repo and show your support!

Announcing Serverless Functions in TypeScript

Β· 5 min read


One of the goals with Juno has always been to make building decentralized, secure apps feel like something you're already used to. No weird mental models. No boilerplate-heavy magic. Just code that does what you expect, without touching infrastructure.

And with this release, we're taking another step in that direction: You can now write serverless functions in TypeScript.

If you're a JavaScript developer, you can define backend behavior right inside your container. It runs in a secure, isolated environment with access to the same hooks and assertions you'd use in a typical Juno Satellite.

No need to manage infrastructure. No need to deploy a separate service. Just write a function, and Juno takes care of the rest.

Cherry on top: the structure mirrors the Rust implementation, so everything from lifecycle to data handling feels consistent. Switching between the two, or migrating later, is smooth and intuitive.


✨ Why TypeScript?​

Rust is still the best choice for performance-heavy apps. That's not changing.

But let's be real: sometimes you just want to ship something quickly. Maybe it's a prototype. Maybe it's a feature you want to test in production. Or maybe you just want to stay in the JavaScript world because it's what you know best.

Now you can.

You get most of the same tools, like:

  • Hooks that react to document or asset events (onSetDoc, onDeleteAsset, etc.)
  • Assertions to validate operations (assertSetDoc, etc.)
  • Utility functions to handle documents, storage, and even call other canisters on ICP

The JavaScript runtime is intentionally lightweight. While it doesn't include full Node.js support, we're adding polyfills gradually based on real-world needs. Things like console.log, TextEncoder, Blob, and even Math.random β€” already covered.


πŸ” Designed for Interop​

The approach to writing serverless functions in Rust and TypeScript is aligned by design. That means if you outgrow your TS functions, migrating to Rust won't feel like starting from scratch. The APIs, structure, and flow all carry over.

Start scrappy, scale gracefully.


πŸ‘€ A Taste of It​

Here's how simple it is to react to a document being created or updated:

import { defineHook, type OnSetDoc } from "@junobuild/functions";

export const onSetDoc = defineHook<OnSetDoc>({
collections: ["posts"],
run: async (context) => {
const data = context.data.data.after.data;
console.log("New post created:", data);
}
});

And here's for example how you'd validate a document before accepting it β€” using Zod for a clean, readable schema:

import { z } from "zod";
import { defineAssert, type AssertSetDoc } from "@junobuild/functions";
import { decodeDocData } from "@junobuild/functions/sdk";

const postSchema = z.object({
title: z.string().min(5, "Title must be at least 5 characters long")
});

export const assertSetDoc = defineAssert<AssertSetDoc>({
collections: ["posts"],
assert: (context) => {
const data = decodeDocData(context.data.data.proposed.data);
postSchema.parse(data);
}
});

That's it. No APIs to expose, no infra to manage. Just code that runs where your data lives.


πŸ–₯️ Local-First Development​

Alongside TypeScript support, we've rethought the local development experience.

Instead of providing a partial local environment, the mindset shifted to mimicking production as closely as possible.

You still get a self-contained image with your Satellite, but now you also get the full Console UI included. That means you can manage and test your project locally just like you would on mainnet.

See this other blog post for all the details.


🧰 Zero Tooling, Just Code​

Here's the beautiful part: even though your serverless functions are written in TypeScript, they're bundled and embedded into a Satellite module that's still compiled in Rust behind the scenes.

But you don't need to install Rust. Or Cargo. Or ic-wasm. Or anything that feels complicated or overly specific.

All you need is Node.js and Docker. The container takes care of the rest: building, bundling, embedding metadata and gives you a ready-to-run Satellite that runs locally and is ready to deploy to production.

In short: just code your functions. The container does the heavy lifting.


πŸ“‘ Already in the Wild​

This isn’t just a feature announcement β€” serverless functions in TypeScript are already live and powering real functionality.

I used them to build the ICP-to-cycles swap on cycles.watch, including all the backend logic and assertions. The whole process was documented over a few livestreams, from setup to deployment.

If you're curious, the code is on GitHub, and there’s a playlist on YouTube if you want to follow along and see how it all came together.

A screenshot of cycles.watch that integrates with OISY and use serverless functions in TypeScript to swap ICP for cycles


▢️ Try It Out​

We've put together docs and guides to help you get started. If you're already using the Juno CLI, you're just one juno dev eject away from writing your first function or start fresh with npm create juno@latest.

To infinite and beyond,
David


Stay connected with Juno by following us on X/Twitter.

Reach out on Discord or OpenChat for any questions.

⭐️⭐️⭐️ stars are also much appreciated: visit the GitHub repo and show your support!

A Production-like Local Dev Environment

Β· 4 min read


Until now, running a local project meant spinning up an emulator with just enough to build with a single default Satellite container for your app.

That worked. But it wasn’t the full picture.

With the latest changes, local development now mirrors the production environment much more closely. You don’t just get a simplified setup β€” you get the actual Console UI, orchestration logic, and almost a full infrastructure that behaves like the real thing.

This shift brings something most cloud serverless platforms don't offer: production-level parity, right on your machine.


πŸ€” Why This Matters​

Local development isn’t just about getting things to run. It’s about understanding how your project behaves, how it scales, and how it integrates with the platform around it.

With this shift, you build with confidence that what works locally will work in production. You don’t need to guess how things will behave once deployed β€” you’re already working in an environment that mirrors it closely.

It also helps you gradually get familiar with the tools that matter, like the Console UI. You learn to use the same workflows, patterns, and orchestration logic that apply when your app goes live.

This removes a lot of friction when switching environments. There's less surprise, less debugging, and a lot more flow.

It’s local development, but it finally feels like the real thing.


πŸ“¦ What’s Included​

The new local environment is powered by the junobuild/skylab Docker image, now used by default in templates and tooling.

It brings together everything you need to build and test your project in a real setup:

  • Console UI β€” the same interface used in production on https://console.juno.build

  • More preinstalled services β€” including Internet Identity, ICP Ledger and Index, NNS Governance and Cycles minting (CMC)

  • Live reload β€” serverless functions written in Rust or TypeScript automatically reload on change

  • Orchestration logic β€” the flow of creating Satellites, managing configurations, and testing behaviors mirrors production

No custom scripts. No extra setup. Everything is handled by the tooling.


πŸ§ͺ What About CI?​

Not every project needs a UI.

That’s why the lightweight junobuild/satellite image still exists β€” and still works just as it always has. It’s ideal for CI pipelines, isolated app testing, or local startup when you don’t need the Console and more infrastructure.

This shift in approach isn’t a breaking change. It adds a new default, but doesn’t remove what was already there.

Looking ahead, there's an intention to simplify scripting even further by allowing Datastore and Storage definitions directly in the main juno.config file. The goal is to eventually phase out juno.dev.config and unify configuration β€” but that’s for the future.

For now, everything remains compatible. You choose what fits best.


πŸš€ How to Get Started​

There are two ways to start using the new local environment β€” whether you’re beginning fresh or updating an existing project.

πŸ†• Starting from Scratch​

Everything is already set up in the templates. Just run:

npm create juno@latest

This will scaffold your project with the latest configuration, using the junobuild/skylab image and the updated local workflow.

πŸ”„ Migrating an Existing Project​

If you already have a project configured for local development and want to switch to the new approach:

  1. Update the CLI:
npm i -g @junobuild/cli
  1. Remove your juno.dev.config.ts (or the JavaScript or JSON equivalent)

  2. Update your docker-compose.yml to use the junobuild/skylab image (adjust paths as needed for your project):

services:
juno-skylab:
image: junobuild/skylab:latest
ports:
# Local replica used to simulate execution
- 5987:5987
# Little admin server (e.g. to transfer ICP from the ledger)
- 5999:5999
# Console UI (like https://console.juno.build)
- 5866:5866
volumes:
# Persistent volume to store internal state
- juno_skylab:/juno/.juno
# Your Juno configuration file.
# Notably used to provide your development Satellite ID to the emulator.
- ./juno.config.mjs:/juno/juno.config.mjs
# Shared folder for deploying and hot-reloading serverless functions
# For example, when building functions in TypeScript, the output `.mjs` files are placed here.
# The container then bundles them into your Satellite WASM (also placed here),
# and automatically upgrades the environment.
- ./target/deploy:/juno/target/deploy/

volumes:
juno_skylab:

That’s it β€” you’re good to go.


βœ… Closing Thoughts

This shift removes a lot of friction between idea and execution.

You build in the same structure, use the same tools, and follow the same workflows you'd use in production β€” but locally, and instantly.

Local development finally feels like you're already in production, just without the pressure.


Stay connected with Juno by following us on X/Twitter.

Reach out on Discord or OpenChat for any questions.

⭐️⭐️⭐️ stars are also much appreciated: visit the GitHub repo and show your support!

What’s New in Juno (November 2024 Edition)

Β· 5 min read

Hey everyone πŸ‘‹

November’s been an exciting month, especially since I’ve officially started working full-time on Juno β€” thanks to the recently announced funding! This shift has already led to delivering some fantastic new features for developers, like automated backups (finally!!!), support for large WASM modules, the ability to buy cycles with Stripe, and a few other goodies.

These updates are all about making development smoother and more efficient, whether you’re building dapps, smart contracts, or managing your projects. Let’s dive into what’s new!


Backups​

To kick things off, I’d like to highlight the introduction of backupsβ€”a feature I’ve been waiting for forever!

This addition brings a crucial layer of security for developers, letting you safeguard your modules and restore them whenever needed.

A screenshot of the steps for an upgrade

Here’s how it works: Currently, one backup per module is supported. You can manage backups manually via both the Console UI and the CLI, with options to create, restore, or delete them. Additionally, backups are automatically created during the upgrade process, taking a snapshot before transitioning to a new version. For those who prefer full control, advanced options let you skip creating a backup or avoid overwriting an existing one.

For anyone who, like me, feels a bit tense whenever it’s time to execute an upgrade, this feature is a huge relief. It’s really a great addition!


Buy Cycles with Stripe​

Getting cycles has become more straightforward, particularly for newcomers and non-crypto-native users, with the ability to buy cycles directly through Stripe, thanks to our friends at cycle.express.

A screenshot of the integration with cycle.express

With this integration, developers can simply make a payment, and the cycles are added directly to their module.


Get ICP directly from the OISY Wallet​

This was both a useful feature, as it makes it easy to transfer ICP from OISY to the developer's wallet on Juno, and an opportunity for me to try out the integration with various ICRC standards I implemented for the foundation.

A screenshot of the integration with OISY

I also used the opportunity to improve the UI/UX of the Receive feature by displaying wallet addresses with a QR code. This update wraps up a few related tasks, such as adding support for sending ICP to the outside world.

A screenshot of the new modal to send ICP to the outside world


Support for Large WASM Modules​

Support for larger WASM modules (over 2MB) has been added. While none of Juno's stock modulesβ€”such as Satellites, Mission Control, or Orbiter (Analytics)β€”come close to this size when gzipped, this limit could quickly be reached by developers using serverless functions.

A screenshot of the upgrade process using the CLI

By extending this limit, developers have more flexibility to embed additional third-party libraries and expand their module capabilities.

This support has been implemented across the CLI, the Console UI, and even local development environments using Docker, ensuring a consistent experience for all workflows.


Default Web Page​

Until recently, new Satellites launched lacked a default page for web hosting. This meant that developers opening their project right after creation would just see a blank page in the browser.

That’s why every new Satellite now comes with a sleek, informative default web pageβ€”delivering a great first impression right out of the box! ✨

A screenshot of the new default web page which contains links to the documentation


Pre- and post-deploy scripts​

Another handy tool introduced this month is support for pre- and post-deploy scripts in the CLI. With this feature, developers can now define a list of commands to be executed at specific stages of the deployment process.

The pre-deploy scripts are perfect for automating tasks like:

  • Compiling assets.
  • Running tests or linters.
  • Preparing production-ready files.

Likewise, post-deploy scripts come in handy for follow-up tasks, such as:

  • Sending notifications or alerts to administrators.
  • Cleaning up temporary files.
  • Logging deployment information for auditing.
import { defineConfig } from "@junobuild/config";

/** @type {import('@junobuild/config').JunoConfig} */
export default defineConfig({
satellite: {
id: "ck4tp-aaaaa-aaaaa-abbbb-cai",
source: "build",
predeploy: ["npm run lint", "npm run build"],
postdeploy: ["node hello.mjs"]
}
});

Darker Dark Theme​

Maybe not the most groundbreaking update, but the dark theme got even darker. πŸ§›β€β™‚οΈπŸ¦‡ Perfect for those late-night coding sessionsβ€”or if you just enjoy the vibe!

A screenshot of the darker dark theme


Improved Documentation​

Another area that saw improvement is the documentation. I aimed to make it more intuitive and useful for both newcomers and experienced developers. That’s why I revamped the guides section. Now, when you visit, you’ll be greeted with a simple question: β€œWhat are you looking to do? Build or Host?” 🎯. This approach should hopefully make onboarding smoother and more straightforward for developers.

The CLI documentation also received an upgrade. Updating it manually was a hassle, so I automated the process. Now, CLI help commands generate markdown files that are automatically embedded into the website every week. No more manual updates for me, and it’s always up to date for you! πŸ˜„

I also dedicated time to documenting all the configuration options in detail, ensuring every setting is clearly explained.

And as a finishing touch, I refreshed the landing page. πŸ‘¨β€πŸŽ¨

I hope these features get you as excited as they got me! I’m already looking forward to what’s next. Speak soon for more updates!

David


Stay connected with Juno by following us on X/Twitter.

Reach out on Discord or OpenChat for any questions.

⭐️⭐️⭐️ stars are also much appreciated: visit the GitHub repo and show your support!

A New Chapter Awakens

Β· 3 min read

A Futuristic cosmic scene with satellites orbiting a distant planet at sunrise, symbolizing renewal and progress in a minimalist space setting


TL;DR

I’ll be working 100% on Juno through 2025.

Hey everyone πŸ‘‹

As you may know, I recently proposed transforming Juno into a Decentralized Autonomous Organization through an SNS swap. Unfortunately, it didn’t reach its funding goal, so Juno didn’t become a DAO.

After the failure, three options came to mind: retrying the ICO with a lower target, continuing to hack as an indie project for a while, or simply killing it.

In the days that followed, I also received a few other options, including interest from venture capitalists for potential seed funding which wasn’t an option for me.

Then, something unexpected happened:

The DFINITY foundation’s CTO, Jan Camenisch, reached out and proposed an alternative: funding the project through 2025.

I took a few days to consider the offer and ultimately accepted.

This support is a tremendous vote of confidence in Juno’s potential and importance within the ecosystem.

It’s worth emphasizing that the foundation’s support comes with no strings attached. They do not receive any stake in Juno, have no preferential treatment, and will not influence decisions. Should I ever consider another SNS DAO or any other funding route in the future, the foundation would have no special allocation or shares. This remains my project, and I am the sole decision-maker and controller.

This support also strengthens the relationship between Juno and the foundation, allowing us to stay in close contact to discuss the roadmap. It’s an arrangement that respects autonomy while fostering collaboration to advance the Internet Computer. As they say, it takes two to tango.

This funding opens up a world of possibilities and marks the first time I’ll work 100% on a project I created. I’m thrilled to continue building Juno as a resource that makes decentralized development accessible and impactful for everyone.

Obviously, while Juno remains under my sole ownership for now, I still believe that Juno should eventually become a DAO. Promoting full control for developers while retaining centralized ownership would be paradoxical. When the time is right, a DAO will ensure that Juno’s growth, security, and transparency are upheld through community-driven governance.

Thank you to everyone who believed in Juno through the SNS campaign and beyond πŸ™πŸ’™. Your support has been invaluable, and this new phase wouldn’t be possible without you. Here’s to what lies aheadβ€”a new chapter indeed.

To infinity and beyond,
David


Stay connected with Juno by following us on X/Twitter.

Reach out on Discord or OpenChat for any questions.

⭐️⭐️⭐️ stars are also much appreciated: visit the GitHub repo and show your support!

Building Tomorrow: Juno Build DAO for the Future

Β· 6 min read

A cover image that contains the title of the blog post and a drawing of a satellite being launched


ICO Failure

The SNS DAO on the Internet Computer failed on Saturday, October 12, 2024 (ICP swap data). As a result, Juno did not become a Decentralized Autonomous Organization (DAO).

Hey everyone πŸ‘‹

I hope you’re all doing well! I’m excited to share some big news with you today. Over the next few weeks, we’re taking some significant steps toward shaping the future of Juno, and I wanted to keep you in the loop about what’s coming.

As you may know, Juno is a blockchain-as-a-service ecosystem that empowers developers to build decentralized apps efficiently. One of its strengths is that it gives developers full and sole control over their work. For this reason, it would be paradoxical to continue operating the platform with a centralized modelβ€”i.e., with me being the sole controller of services, such as the administration console or our CDN. That’s why I’m thrilled to unveil that, in the upcoming weeks, I’m aiming to fix this bias by proposing that Juno becomes a Decentralized Autonomous Organization (DAO).

While this potential shift is on the horizon, there are a few key steps you can take to stay informed and involved in the process. Here’s how you can help shape the future of developing on Web3:


1. Sign Up to Join the Party​

To ensure you don’t miss any crucial updates, I encourage you to sign up for our newsletter. The journey to proposing a DAO and making it a reality involves multiple steps, each requiring your participation. By signing up, you’ll receive timely notifications whenever there’s an opportunity to get involved and make a real impact.


2. Get Informed​

note

The white paper has been updated to continue presenting the vision; however, the tokenomics aspect has been notably removed, as it is no longer relevant following the failure of the SNS DAO.

I’ve put together a white paper that outlines the reasoning and vision I have for a Juno Build DAO. I highly recommend giving it a read to fully understand what I’m aiming to achieve.

If you have any questions, feel free to reach out on Discord, Twitter/X, OpenChat or also the DFINITY forum (as requested per their guidelines).

Questions are always welcome at any time, but if you’re looking to engage directly, I’ll be hosting a Juno Live session on 9 September at 3:00 PM CET. Join the livestream on YouTube to interact in real-time.


3. Cast Your Vote​

info

The proposal was approved and executed on September 26, 2024.

While I typically avoid relying on third parties for core features, transforming Juno into a DAO without leveraging such services would be an immense task. That’s why I’m proposing to use the Internet Computer’s built-in solution for creating and maintaining DAOs, known as SNS.

To kickstart the process of transforming our ecosystem, this framework requires submitting a proposal to the Internet Computer’s governance, known as NNS. This step ensures a decentralized and democratic process. It also prepares for the handover of control of the smart contracts and allows all participants to review the parameters involved.

Once this proposal is live, your voice will be crucial! You’ll have the opportunity to vote on whether to accept or reject it.

Without your approval, the ICO won’t even start.


4. Participate in the ICO​

Disclaimer

Please note that the following does not constitute financial advice.

If the proposal is approved, an initial decentralization swap will be kicked off. The goal here is to raise the initial funds for the DAO and to decentralize the voting power. Think of it like crowdfunding, where people contribute ICP tokens. In return, participants are rewarded with staked tokens, giving them a share of the DAO's voting power.

For the swap to succeed, it requires at least 100 participants and 200,000 ICP tokens. Otherwise, the entire journey of transforming Juno into a DAO fails. So, if you’re excited about being part of this adventure, this could be the step where you make a real difference β€” if you decide on your own will to do so.


5. Outcome​

If the swap fails, it will mark the beginning of the end. While the platform won’t be deprecated immediately, I will gradually phase it out over the course of, let's say, a year. During this time, Juno will stop accepting new developers, and I will no longer develop new features, promote the eco-system, or invest in it beyond maintenance.

For those already using Juno, I want to reassure you that I won’t leave you stranded. I’m committed to offering support to help you transition and find suitable alternatives. I’m not, I hope, that much of an a-hole. I try to maintain good karma.

On a personal note, I would also be deprecating all of my personal projects, such as proposals.network, as I have no intention of using any developer tooling other than Juno for my own Web3 projects.

If the swap is successful, hooray! Juno will officially become a DAO, allowing you to actively participate in the governance of the project and start using the new JUNOBUILD token, among other exciting developments.

This will mark the beginning of an exciting new chapter, with the community at the heart of Juno's future.

To infinity and beyond,
David


Useful Links:

How to Redirect a Route After Renaming It

Β· 5 min read

Photo by Nick Fewings on Unsplash


Introduction​

Renaming a route in your web application is a common task, but it’s crucial to handle it correctly to avoid breaking links and negatively impacting your SEO. Redirecting the old route to the new one ensures a seamless user experience and maintains your site's search engine rankings.

In this blog post, we’ll guide you through the steps to set up a redirection after renaming one of your pages.

Unleashing the Infinite with Serverless

Β· 8 min read


Hey there πŸ‘‹,

Are you looking to extend Juno's features? Stop right there, because it is now possible!

I'm thrilled to unveil today's new addition to the set of features offered by Juno: the introduction of serverless Functions enabling developers to extend the native capabilities of Satellites. This groundbreaking update opens a plethora of opportunities for developers to innovate and customize their applications like never before.


What Are Serverless Blockchain Functions?​

In the realm of cloud computing, serverless architecture allows developers to build and run applications and services without the burden of managing infrastructure. This model enables the execution of server-side code based on user demand, allowing for direct interactions with APIs, databases, and other resources as part of your project's deployment. It's a paradigm that significantly reduces overhead and increases the agility of software development processes.

The introduction of serverless blockchain functions by Juno innovatively takes this concept a step further by integrating blockchain technology into this flexible and efficient framework. This groundbreaking development opens the door for extending the native capabilities of Satellites smart contracts, pushing the boundaries of what's possible within the blockchain space.

This means you can now enhance the functionality of Satellites smart contracts and extend those capabilities with anything that can be achieved on the Internet Computer blockchain.


How Does It Work?​

At the core of Juno's serverless blockchain functions are hooks, which are essentially the backbone of how these functions operate within the ecosystem. These hooks are defined to automatically respond to event triggers related within your Satellite, including operations such as creating, updating, and deleting to documents and assets.

An essential feature of these optional hooks is their ability to spawn asynchronously, a design choice that significantly enhances the efficiency and responsiveness of applications built on Juno. This asynchronous spawning means that the hooks do not block or delay the execution of calls and responses between your client-side decentralized application (dApp) and the smart contract.

A picture is worth a thousand words, so here is a simplified schematic representation of a hook that is triggered when a document is set:


Getting Started​

In addition to unveiling this new feature, we're also excited to introduce a brand-new developer experience we hope you're going to enjoy. This is built on the local development environment we released earlier this year, designed to make your work with Juno smoother and more intuitive.

note

Make sure you have Juno's CLI tool installed on your machine.

Start by ejecting the Satellite within your project. This step prepares your project for local development. Open your terminal and run:

juno dev eject

In a new terminal window, kick off the local development environment that leverages Docker:

juno dev start

Now, your local development environment is up and running, ready for you to start coding.

Once you're ready to see your changes in action, compile your code:

juno dev build

One of the key benefits of Juno's local development environment is its support for hot reloading. This feature automatically detects changes to your code and deploys them in the local environment. It means you can immediately test your custom code locally, ensuring a fast and efficient development cycle.


Demonstrating Hooks and Data Operations​

This sample application illustrates the use of Juno's serverless functions to perform asynchronous data operations with a small frontend client and backend hook setup.

The frontend client is designed to save a document in the Datastore, while the backend hook modifies this document upon being triggered. This process exemplifies the asynchronous capability of functions to read from and write to the Datastore.

Getting the Sample​

To begin exploring this functionality, clone the example repository and prepare the environment with the following commands:

git clone https://github.com/junobuild/examples
cd rust/hooks
npm ci

After setting up the project, to start and debug the sample in your local environment, please follow the steps as outlined in the previous chapter Getting Started.

Hook Implementation Details​

The core of this sample is the hook code, which is triggered upon the document set operation in a specific collection. Here’s the hook's logic:

#[on_set_doc(collections = ["demo"])]
async fn on_set_doc(context: OnSetDocContext) -> Result<(), String> {
// Decode the new data saved in the Datastore
let mut data: Person = decode_doc_data(&context.data.data.after.data)?;

// Modify the document's data
data.hello = format!("{} checked", data.hello);
data.yolo = false;

// Encode the data back into a blob
let encode_data = encode_doc_data(&data)?;

// Prepare parameters to save the updated document
let doc: SetDoc = SetDoc {
data: encode_data,
description: context.data.data.after.description,
updated_at: Some(context.data.data.after.updated_at),
};

// Save the updated document
set_doc_store(
context.caller,
context.data.collection,
context.data.key,
doc,
)?;

Ok(())
}

This hook demonstrates asynchronous processing by reading the initial data saved from the frontend, modifying it, and then saving the updated version back to the Datastore. It's triggered specifically for documents within the "demo" collection and showcases how to handle data blobs, execute modifications, and interact with the Datastore programmatically.


Unlocking Anything on the Internet Computer​

As mentioned in the introduction, the serverless functions extend Juno's capabilities to anything that can be achieved on the Internet Computer. With this in mind, let's explore implementing HTTPS outcalls to a Web2 API in another sample.

Getting the Sample​

To explore this advanced functionality, follow the steps below to clone the repository and set up the project:

git clone https://github.com/junobuild/examples
cd rust/https-outcalls
npm ci

After cloning and navigating to the correct directory, proceed with starting and debugging the sample in your local environment, as outlined in the Getting Started chapter.

Hook Implementation Details​

The hook implemented in this sample interacts with the Dog CEO API to fetch random dog images and update documents within the dogs collection in the Datastore. Here's how it works:


// The data of the document we are looking to update in the Satellite's Datastore.
#[derive(Serialize, Deserialize)]
struct DogData {
src: Option<String>,
}

// We are using the Dog CEO API in this example.
// https://dog.ceo/dog-api/
//
// Its endpoint "random" returns such JSON data:
// {
// "message": "https://images.dog.ceo/breeds/mountain-swiss/n02107574_1118.jpg",
// "status": "success"
// }
//
// That's why we declare a struct that matches the structure of the answer.
#[derive(Serialize, Deserialize)]
struct DogApiResponse {
message: String,
status: String,
}

#[on_set_doc(collections = ["dogs"])]
async fn on_set_doc(context: OnSetDocContext) -> Result<(), String> {
// 1. Prepare the HTTP GET request
let url = "https://dog.ceo/api/breeds/image/random".to_string();

let request_headers = vec![];

let request = CanisterHttpRequestArgument {
url,
method: HttpMethod::GET,
body: None,
max_response_bytes: None,
// In this simple example we skip sanitizing the response with a custom function for simplicity reason.
transform: None,
// We do not require any particular HTTP headers in this example.
headers: request_headers,
};

// 2. Execute the HTTP request. A request consumes Cycles(!). In this example we provide 2_000_000_000 Cycles (= 0.002 TCycles).
// To estimate the costs see documentation:
// - https://internetcomputer.org/docs/current/developer-docs/gas-cost#special-features
// - https://internetcomputer.org/docs/current/developer-docs/integrations/https-outcalls/https-outcalls-how-it-works#pricing
// Total amount of cycles depends on the subnet size. Therefore, on mainnet it might cost ~13x more than what's required when developing locally. Source: https://forum.dfinity.org/t/http-outcalls-cycles/27439/4
// Note: In the future we will have a UI logging panel in console.juno.build to help debug on production. Follow PR https://github.com/junobuild/juno/issues/415.
//
// We rename ic_cdk::api::management_canister::http_request::http_request to http_request_outcall because the Satellite already includes such a function's name.
match http_request_outcall(request, 2_000_000_000).await {
Ok((response,)) => {
// 3. Use serde_json to transform the response to a structured object.
let str_body = String::from_utf8(response.body)
.expect("Transformed response is not UTF-8 encoded.");

let dog_response: DogApiResponse =
serde_json::from_str(&str_body).map_err(|e| e.to_string())?;

// 4. Our goal is to update the document in the Datastore with an update that contains the link to the image fetched from the API we just called.
let dog: DogData = DogData {
src: Some(dog_response.message),
};

// 5. We encode those data back to blob because the Datastore holds data as blob.
let encode_data = encode_doc_data(&dog)?;

// 6. Then we construct the parameters required to call the function that save the data in the Datastore.
let doc: SetDoc = SetDoc {
data: encode_data,
description: context.data.data.after.description,
updated_at: Some(context.data.data.after.updated_at),
};

// 7. We store the data in the Datastore for the same caller as the one who triggered the original on_set_doc, in the same collection with the same key as well.
set_doc_store(
context.caller,
context.data.collection,
context.data.key,
doc,
)?;

Ok(())
}
Err((r, m)) => {
let message =
format!("The http_request resulted into error. RejectionCode: {r:?}, Error: {m}");

Err(message)
}
}
}

This sample not only provides a practical demonstration of making HTTP outcalls but also illustrates the enhanced capabilities that serverless functions offer to developers using Juno.


Conclusion​

In conclusion, Juno's serverless functions mark a significant advancement in blockchain development, offering developers the tools to create more sophisticated and dynamic applications. This feature set not only broadens the scope of what can be achieved within Juno's ecosystem but also underscores the platform's commitment to innovation and developer empowerment. As we move forward, the potential for serverless technology in blockchain applications is boundless, promising exciting new possibilities for the future.

πŸ‘‹


Stay connected with Juno by following us on X/Twitter.

Reach out on Discord or OpenChat for any questions.

⭐️⭐️⭐️ stars are also much appreciated: visit the GitHub repo and show your support!

Local development is here!

Β· 4 min read


Hello πŸ‘‹,

I'm excited to share that local dApp development and end-to-end testing are now available on Juno through our new Docker image.

This update serves as a key addition to our upcoming features for the year to come, offering developers a practical solution to build or test their projects in a sandbox environment.

The documentation for this new feature is available here. Moreover, the container's code is open source, and you can access it here for more insights.

In this blog post, rather than reiterating the documentation, I'll provide an example to demonstrate how you can effectively utilize this feature in your development workflow.


Before you begin​

Make sure you have Docker installed on your machine.


Clone the example​

I've prepared a sample project to demonstrate how a dApp can be run and persist data in a local environment. Open your terminal and clone the sample project developed with Astro:

git clone https://github.com/junobuild/examples/
cd examples/astro
npm ci

Run the Docker Image​

To start the container, head to the subfolder containing the configuration I prepared for you.

cd docker

In this folder, you will find two files. A docker-compose.yml file contains essential information for the image, such as the port and a volume. For more details, refer to the documentation.

docker-compose.yml
services:
juno-satellite:
image: junobuild/satellite:latest
ports:
- 5987:5987
volumes:
- astro_dapp:/juno/.juno
- ./juno.dev.json:/juno/juno.dev.json

volumes:
astro_dapp:

There's also a juno.dev.json file, which is designed to set up a collection once the Satellite is populated locally, similar to what you can do in Juno's administration console.

juno.dev.json
{
"satellite": {
"collections": {
"db": [
{
"collection": "counter",
"read": "managed",
"write": "managed",
"memory": "stable"
}
]
}
}
}

Given that everything is set for you, you can run the following command to start the container:

docker compose up

And that's it! The container is designed to manage serving a local Internet Computer replica. It also embeds a custom CLI which handles deploying and populating the Internet Identity and a Satellite. With this setup, you have everything necessary for efficient local development.


Run the dApp​

To get the sample dApp up and running, open another terminal window. Navigate back to the root folder of the project and start the dApp using the following command:

npm run dev

This project leverages our Vite Plugin and libraries, streamlining the setup process with minimal configuration needed. I've already configured it for you, but typically, you would only need to set a container option for the plugin:

astro.config.js
import { defineConfig } from "astro/config";
import juno from "@junobuild/vite-plugin";

export default defineConfig({
vite: {
plugins: [
juno({
container: true
})
]
}
});

And pass along the environment variable to the initialization:

await initSatellite({
satelliteId: import.meta.env.PUBLIC_SATELLITE_ID,
container: import.meta.env.PUBLIC_CONTAINER
});

With the local dApp server active, you can now access it in your browser at http://localhost:4321.

Upon visiting the site, you'll find an option to sign in using Internet Identity. Since everything is deployed locally in a sandbox, your existing identity (anchor) won't be recognized, that's why you will have to create a new one.

A screenshot that showcases the sample dApp home screen

Once signed in, you'll see a "count" action, a simple feature that increments a counter and saves a value associated with your identity.

A screenshot that showcases the sample dApp once signed in

This demonstrates the dApp's capability to interact with local data and user identities.


Conclusion​

In conclusion, the integration of local development capabilities with Juno, using Docker, marks a significant step forward in streamlining and simplifying the development process for dApps.

This setup not only facilitates a more efficient development cycle but also offers a practical environment for thorough testing. It's a straightforward, no-frills approach that underscores Juno's commitment to improving the developer experience without overcomplicating the process.

I am excited to see the innovative applications and solutions the developers of our community will create with these enhanced tools at their disposal.

πŸ‘‹


Stay connected with Juno by following us on X/Twitter.

Reach out on Discord or OpenChat for any questions.

⭐️⭐️⭐️ stars are also much appreciated: visit the GitHub repo and show your support!

Setting Up Your Internet Identity on internetcomputer.org

Β· 5 min read


Web 2.0 uses usernames and passwords for logging into websites, which isn't very secure, can be a hassle, and doesn't always keep your information private.

Internet Identity (II), on the other hand, is a better way to log in online. Instead of just usernames and passwords, it uses new technology like fingerprints or Face ID on your phone or special hardware devices to make sure it's really you when you log in.

This authentication provider is used by Juno to secure access to its administration console for developers.

While most decentralized applications on the Internet Computer use the default domain ic0.app for the registration process using Internet Identity (referred to as the "old domain" in this article), Juno defaults to the more recognizable, aesthetically pleasing and easy to remember domain internetcomputer.org.

However, there is a caveat to this choice. When you register with Internet Identity, it's tied to a specific domain. This means that if you have previously created an identity to access other decentralized apps on the Internet Computer, you may not be able to sign in to Juno's console seamlessly using the main "call to action". You won't be blocked from logging in; the login screen also supports the old domain. However, the option to use it is not as prominent.

That's why in this article, we will guide you through the process of ensuring your identity works seamlessly on both domains. By adding a passkey for the other domain, you will ensure that your identity is available for sign-in, regardless of those two choices.

note

In the following instructions, we assume that your identity works on identity.ic0.app, and we are demonstrating how to add it for identity.internetcomputer.org. Of course, this tutorial also applies in reverse; you can simply switch the domain while following these steps.


Step-by-Step Guide​

Here are the steps to follow:

1. Sign-in Internet Identity​

As a first step, you need to login into Internet Identity. Go to https://identity.ic0.app.

2. Add passkey​

As we are looking to register a passkey for the other domain on your device, initiate the process by clicking "Add passkey".

Initiate add passkey screenshot

3. Copy the URL​

Internet Identity will present you with various user-friendly options for registering a new passkey. While these options are handy if you plan to add an additional browser or device, for the purpose of this tutorial, our goals are different.

Therefore, please ignore the information on the screen and only copy the code that is presented to you.

Copy url screenshot

4. Navigate to modified URL​

We are assuming that you want to register the other domain for the same browser. In that case, open a new tab and paste the URL you just copied but, before pasting the link, modify the domain part to point to the other domain.

For example, change a copied link from https://identity.ic0.app/?action=add-passkey&ii=2279219 to https://identity.internetcomputer.org/?action=add-passkey&ii=2279219 by replacing ic0.app with internetcomputer.org.

Note that along the way, you will be prompted to authenticate yourself with your authentication method, such as fingerprint or Face ID. This ensures a secure sign-in on the other domain as well.

Verify new passkey screenshot

5. Enter verification code​

To validate the new passkey, you will be prompted to verify a code in the original tab where you initiated the creation of the new passkey.

Simply select and copy the verification code displayed on the screen.

Copy verification code screenshot

Return to the previous tab and enter the code. You can either manually type it or, once you've placed your cursor in the first digit field, paste the entire code (Ctrl|Cmd+V), which will be automatically filled.

Enter verification code screenshot

6. Confirmation​

Congratulations, the passkey on the device you just used for this tutorial is now ready for both the old and other domain.

Success screenshot

7. Optionally, rename the new passkey​

The new passkey will inherit a default name. Optionally, you can rename it to make it clear in the future which one is related to which domain. To do this, continue to Internet Identity, select "Rename", and follow the instructions provided.

Rename passkey screenshot


Conclusion​

In this guide, we've walked you through the process of setting up your Internet Identity for another domain. We hope this has been helpful and will provide you with easy access to Juno's administration console and other dApps using multiple domains for sign-in with Internet Identity.

πŸ‘‹

Stay connected with Juno by following us on X/Twitter.

Reach out on Discord or OpenChat for any questions.

⭐️⭐️⭐️ stars are also much appreciated: visit the GitHub repo and show your support!