# Juno Juno is your self-contained serverless platform for building full-stack web apps without DevOps or backend boilerplate. Developers use their favorite frontend frameworks like React, SvelteKit, or Next.js, and write backend logic in Rust or TypeScript as serverless functions. Everything is bundled into a single WebAssembly (WASM) artifact that runs in a decentralized, stateful environment — under full user ownership — on the Internet Computer. Juno cannot access or modify your code, data, or infrastructure. It supports GitHub Actions for deploys and upgrades, and provides both a CLI and web Console UI for managing projects. The local development environment closely mirrors production, ensuring smooth transitions from build to deployment. # Create a Satellite When you're ready to deploy your project to production, you'll need to create a [satellite](/docs/terminology.md#satellite). 1. To get started, sign-in to the Juno [console](https://console.juno.build). If you are a new developer on Juno and the Internet Computer, you may be prompted to create your first anonymous [Internet Identity](/docs/terminology.md#internet-identity). 2. Click **Launch a new satellite**. 3. Enter a name for your satellite (note: this is for display purposes only and does not need to be unique). 4. Confirm with **Create a Satellite.** 5. The platform will then create your satellite and provision its resources. 6. Once the process is complete, click **Continue** to access the overview page. 🎉 You’re all set! You can now deploy your frontend app, static website, or publish your serverless functions to production. ➡️ Continue with the [deployment](/docs/category/deployment.md) guides to take the next step. # FAQ ### Where do I find support? For help and questions about best practices, join our [Discord](https://discord.gg/wHZ57Z2RAG) channel. You can report issue or bug on [GitHub](https://github.com/junobuild/juno). ### How much does it cost? Getting started is free. Developers are responsible for operating costs. Transaction costs may apply for some features. [Detailed information and estimations](/docs/pricing.md) are available. ### How do I verify an upgrade? Before approving an upgrade, developers can verify the proposed changes by checking the release details on [GitHub](https://github.com/junobuild/juno/releases). Each release includes a list of modules proposed for update, along with a checksums.txt file that contains the SHA-256 hashes of the module binaries. For example: ``` 68c1978c4fe7ad98cc95fd73e20f42feaf66f010e8fe91a7047116001dfcab13 ./console.wasm.gz31647e69cd5a3639bda65300e37a8f44eb5feb3562e81f29c1ab17a31a867b42 ./mission_control.wasm.gz87a18c56889690a05adf2b4289b911714c0ac6449108ae0c588203680c2c54d2 ./observatory.wasm.gz5a74b1224a5a5d14e5d9f0ebe49a4ba6d51780dbde983525b5ef16a976c28f14 ./orbiter.wasm.gz40b77e22e13aee86ac3872352640443fa27a9bdc098847f15bfafe844a9f58ab ./satellite.wasm.gz ``` If the hash of a module differs from the one listed in the release, the upgrade should not be approved. It's also important to check the release notes to confirm which modules are actually included in the update, as the build process always prints all hashes. Developers can also validate the data by querying the [CDN](https://github.com/junobuild/cdn), which provides each module with a certificate. Since the CDN itself is backed by a Juno Satellite, this guarantees that the delivered WebAssembly modules have not been tampered with. For reproducibility, developers can run the official Docker build for Juno and its modules. If everything matches, the same versions should be produced. ### What happens if Juno disappears? In the unlikely event of Juno's disappearance, you, as the sole controller of your [mission control](/docs/terminology.md#mission-control) and [satellites](/docs/terminology.md#satellite), would retain full control over your creations. They would continue to function independently without any reliance on Juno. ### Can I just deploy my website on the Internet Computer? Absolutely! With Juno, you have the flexibility to choose the level of functionality you want for your project. Whether you simply want to host your static website on the [Internet Computer](https://internetcomputer.org/) or take advantage of Juno's rich features like [authentication](/docs/build/authentication.md), [datastore](/docs/build/datastore.md), and [storage](/docs/build/storage.md) for building dynamic dapps, the choice is yours. ### Is Juno a project of the DFINITY foundation? No, Juno is an independent project. In 2024, we received a [Developer Grant](https://dfinity.org/grants/) from the [DFINTIY foundation](https://dfinity.org) to grow the ecosystem, ease developer onboarding, and enhance visual communication. For 2025, the Foundation is funding the project to further expand the developer ecosystem and advance the Internet Computer. ### Does Juno exercise control over developers' work? No, Juno does not exert any control over developers' work. Juno is designed to provide developers with true control and autonomy over their projects. Developers have full ownership and control over their [mission control](/docs/terminology.md#mission-control), [satellites](/docs/terminology.md#satellite), and the applications they build on the platform. Juno's philosophy aligns with the principles of Web3, empowering developers with transparency, decentralization, and the freedom to create and innovate without external interference. ### How does Juno differ from Firebase? Besides the fundamental differences between the Web2 and Web3 approaches, which empower developers using Juno with true control and future governance over their work, there are additional distinctions that apply regardless of the underlying philosophy. #### Source Firebase is a closed-source Backend as a Service that restricts access to the underlying details and limits the ability to make custom modifications. In contrast, Juno is fully [open-source](https://github.com/junobuild/), providing transparency and the flexibility to customize as needed. #### Pricing Firebase follows a usage-based pricing model, where costs are calculated based on factors like request volume and data storage. The absence of a cap-setting option is a cause for concern, as unexpected spikes in usage can result in substantial expenses. In contrast, Juno takes a different approach. Developers pre-charge their smart contracts with [cycles](/docs/terminology.md#cycles), which are then utilized to cover computation and memory usage of the [satellites](/docs/terminology.md#satellite) and [mission control](/docs/terminology.md#mission-control). This mechanism eliminates the risk of encountering unexpected financial burdens due to unforeseen usage surges. ### Do you have a library for \[some other language\]? We officially support [JavaScript](/docs/setup-the-sdk.md) for anything frontend. Extending smart contract capabilities is done in [Rust](/docs/build/functions.md). Community-supported libraries and contributions are warmly welcomed. ### Where can I find resources about Juno? The [documentation](/docs/intro.md) is a great starting point to explore Juno. Additionally, the [blog](https://juno.build/blog) provides insightful tutorials for various frameworks. You can also check out the [guides](/docs/category/guides.md), which feature examples to help you get started. --- Do you have more questions or need further assistance? Feel free to reach out to us on [Discord](https://discord.gg/wHZ57Z2RAG) or [Twitter](https://twitter.com/junobuild). We're here to help! # Getting Started with Juno Unless you're looking to solely host your static website, the recommended way to start with Juno is by developing locally using the emulator — a production-like environment with full support for data, authentication, storage, and serverless functions. It gives you everything you need to build and test your app before deploying anything live. Here are a few solid places to go from here: * 🚀 [Start a new project](/docs/start-a-new-project.md) – Scaffold a brand new project with your favorite frontend framework. * 🔌 [Set up the SDK](/docs/setup-the-sdk.md) – Integrate Juno into an existing app. * 🧪 [Run your project locally](/docs/guides/local-development.md) – Use the emulator to build and test locally in an environment that mirrors production. * 🛰️ [Deploy with a Satellite](/docs/create-a-satellite.md) – When you're ready to go live, deploy your project to its own container. --- ## How It Works Juno is your own self-contained execution space. No DevOps. No backend boilerplate. No surprise complexity. You build your frontend using the frameworks you love — React, SvelteKit, Next.js, you name it. Need backend logic? Just drop in a serverless function written in Rust or TypeScript. Everything gets bundled into a single deployable WebAssembly (WASM) container. One artifact. One push. That's your app. It runs in an unstoppable environment that holds its entire state — data, logic, and storage. And here's the beauty of it: Juno controls nothing. It has zero access to your code, data, or infrastructure. Everything runs under your ownership. Think of it as the space between self-hosting and the serverless cloud — a reimagined model for application development. You manage your projects and supporting modules — themed around space mythology — using either a CLI or the Console UI, depending on your workflow. To strengthen this principle of non-interference, deploys and upgrades can be handled via GitHub Actions if you choose to opt in — which themselves can't start or stop your app once it's live. And during development, the environment mirrors production as closely as possible — so you're never caught by “but it worked locally.” --- ## Further Details Learn more about the available products, from auth and data to hosting and functions. * [Authentication](/docs/build/authentication.md) * [Datastore](/docs/build/datastore.md) * [Storage](/docs/build/storage.md) * [Hosting](/docs/build/hosting.md) * [Functions](/docs/build/functions.md) * [Analytics](/docs/build/analytics.md) * [Monitoring](/docs/management/monitoring.md) * [Snapshots](/docs/management/snapshots.md) # Pricing There are two types of costs associated with using Juno. 1. Operating costs for your [mission control](/docs/terminology.md#mission-control), satellites and orbiters 2. Costs for executing transactions with Juno **Note:** Last update: Dec. 3, 2024 ## Operating costs As the owner of your mission control, satellites and orbiters, you are responsible for their operating costs. To ensure that your infrastructure usage is covered, you must maintain a minimum balance of [cycles](/docs/terminology.md#cycles). You can top up your cycle balance in the Juno [console](https://console.juno.build/) through one of the following methods: * Using ICP from your wallet. * Purchasing cycles with Stripe, thanks to our friends at [cycle.express](https://cycle.express). * Transferring cycles between modules, such as moving cycles from one Satellite to another. ## Transaction costs New developers who join Juno are granted credits to create a mission control and their initial [satellite](/docs/terminology.md#satellite). To create additional satellites, a fee of 0.4 ICP is necessary, along with the infrastructure costs for setting up the smart contract. Similarly, enabling analytics by creating an [orbiter](/docs/terminology.md#orbiter) entails a fee of 0.4 ICP, plus the infrastructure costs for creating the smart contract. Please note that additional transaction fees may be introduced in the future, and pricing and models are subject to change. ## Estimating Costs Below are a few examples of costs provided for explanatory purposes only and without guarantee. Please note that these cost estimates are subject to change and may vary depending on network conditions and usage patterns. **Tip:** You can use the [Pricing Calculator](https://internetcomputer.org/docs/current/developer-docs/cost-estimations-and-examples) to get a better rough estimate of how much your project might cost. ### Storage The estimated annual cost of storing 1 gigabyte of data in a smart contract is $5. To calculate the estimated monthly cost for 1 gigabyte of storage, you can refer to the table provided on the Internet Computer [website](https://internetcomputer.org/docs/current/developer-docs/gas-cost). | Transaction | 13-node Application Subnets | 34-node Application Subnets | | --- | --- | --- | | GB Storage Per Second | $0.000000169749 | $0.000000443960 | | Derived to a 30-day month | $0.439 | $1.149 | | Derived to a 12-month year | $5.268 | $13.788 | ### Deployment Based on our experimentation, deploying an entire website on-chain, such as the website [http://juno.build](http://juno.build), which consists of approximately 900 files (including compressed versions of the files) and is 40 MB in size, is estimated to cost around 0.114 T Cycles, which converts to 0.0105 ICP ($0.15). It's important to note that subsequent deployments of your project can have significantly lower costs if the build consistency of your application is maintained. Juno only uploads new files to your satellites, which helps reduce costs compared to initial deployments. ### Data Querying data on the Internet Computer is currently free, so there are no additional costs to expect when reading data. In terms of persisting data, based on our experience, storing 100 instances of a JSON sample data with approximately 90 fields, totaling around 900 bytes, costs approximately 0.0005 TCycles or 0.00017 ICP ($0.000675). This means that the cost for a single transaction of this nature would be approximately 0.000005 TCycles or 0.0000017 ICP ($0.00000675). # Setup the SDK To connect your app to a Satellite and use Juno's features — like authentication, data, storage, and serverless functions — you'll need to initialize the SDK. This guide walks you through how to do that, whether you're using a plugin (Next.js, Vite) or setting things up manually. **Info:** If you intend to use Juno solely for **[hosting](/docs/build/hosting.md)** purposes, you may skip the following steps. --- ## Initialization 1. Install the Juno SDK: * npm * yarn * pnpm ``` npm i @junobuild/core ``` ``` yarn add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` ``` pnpm add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` 2. Initialize your satellite in your web app: ``` import { initSatellite } from "@junobuild/core";await initSatellite(); ``` It is generally recommended to initialize globally the library at the top of your application. --- ## Configuration To connect your application with a container (your Satellite) and other necessary resources, some configuration is required. This setup ensures your application can securely access and initialize the correct Satellite instance, environment variables, and key settings. For most developers, the [Next.js](/docs/reference/plugins.md#nextjs-plugin) or [Vite](/docs/reference/plugins.md#vite-plugin) plugins streamline this process by managing environment variables automatically. However, if you're not using one of these plugins, a bit of manual configuration is needed. ### Automated When using the plugins, the configuration handled through a `juno.config` file (TypeScript, JavaScript, or JSON) at your project root is automatically injected within your application. The plugins read the file and automatically load the required information when you build and run your dApp. Juno's provided templates come preconfigured with these plugins, so you can quickly get started. For details on setting up the plugin yourself, refer to the [Next.js](/docs/reference/plugins.md#nextjs-plugin) or [Vite](/docs/reference/plugins.md#vite-plugin) documentation. To scaffold a minimal `juno.config` file, run: ``` npx @junobuild/cli init --minimal ``` ### Manually configure your application If you aren't using a plugin, you can still configure your application by providing the `satelliteId` directly. In this case, add or update the following in your configuration file: ``` import { initSatellite } from "@junobuild/core";await initSatellite({ satelliteId: "your-actual-satellite-id"}); ``` Replace `your-actual-satellite-id` with your actual ID, available in the [console](https://console.juno.build) on the overview page. # Start a new project Are you starting a fresh project? We've got your back. Whether it's a website, blog, or app, leverage Juno's onboarding CLI to scaffold your project with ready-made templates for popular frontend frameworks like Astro, Next.js, React, SvelteKit, Vue, and Angular. Just run: * npm * yarn * pnpm ``` npm create juno@latest ``` ``` yarn create juno ``` ``` pnpm create juno ``` ...and follow the prompts. --- This will generate a working project with: * A frontend app using your selected framework * Preconfigured emulator support * Sample serverless functions (TypeScript or Rust) * Built-in auth, data, and storage integration * Or a minimal template if you just want to host a static site You can run the project locally right away, explore the code, and start building. # Terminology In Juno, we use some terms that may be unfamiliar to some. This page provides a summary of the most commonly used terms. ## Account Identifier An "Account Identifier" is an address, serving as the textual representation of an account on the Internet Computer (ICP) ledger. It can represent an account owned by an individual or a smart contract. ## Console The "console" refers to Juno's administration application, located at [https://console.juno.build](https://console.juno.build). ## Controller On the Internet Computer, a controller is a ([principal](#principal)) (such as a user or service) that has full administrative control over a deployed module. In earlier versions of Juno, the term _controller_ was used to describe access permissions to [mission controls](/docs/terminology.md#mission-control), [satellites](/docs/terminology.md#satellite) and [orbiters](/docs/terminology.md#orbiter). This concept has since been replaced by access keys, which provide more flexibility and support for different roles. For current usage and setup, refer to the [Access Keys documentation](/docs/miscellaneous/access-keys.md). ## Cycles Cycles are used to pay for [infrastructure](/docs/white-paper/infrastructure.md) usage. Your [mission control](/docs/terminology.md#mission-control) or [satellite](/docs/terminology.md#satellite) consumes cycles while it's active. The amount of cycles available determines whether a smart contract will be active, inactive, or eventually decommissioned (deleted). This ensures that related costs cannot surpass the amount of cycles available. Think of cycles like prepaid mobile data: * Just like your mobile plan allows you to make calls and browse the internet, cycles enable your smart contracts to process computations and store data. * When your data (cycles) runs out, your service (smart contract) becomes inactive. * To keep your smart contract running smoothly, you need to top up your cycles regularly. * If you don’t top it up, after some time, it will be decommissioned, similar to losing your prepaid number due to prolonged inactivity. Learn more about [computation and storage costs](https://internetcomputer.org/docs/current/developer-docs/gas-cost). ## Doc "doc" is a commonly used shorthand in Juno for a "document of the Datastore". Wherever you see the term "doc" in the codebase or documentation, it refers specifically to a document entity managed by the [Datastore](/docs/build/datastore.md). This abbreviation is used for brevity and consistency throughout the project. ## ICP The ICP token is the cryptocurrency used to pay for transactions on Juno's [infrastructure](/docs/white-paper/infrastructure.md). It can also be converted into cycles, which are used to pay for computation and storage. Unlike the market price of ICP, the price of cycles remains constant, ensuring predictable costs for infrastructure usage. ## Internet Identity [Internet Identity](https://internetcomputer.org/internet-identity) is a decentralized authentication provider that offers a secure blockchain login experience with a user-friendly Web2 interface. It is free and passwordless. It integrates WebAuthn for maximum compatibility and, unlike other Web3 authentication solutions, it does not require you to save a private key on your device or in an application. ## Mission control A mission control is a dedicated smart contract designed for managing your modules. It is under your exclusive control, allowing it, for example, to hold ICP and perform various operations such as topping up your modules. Think of it like the command center for a space mission. Just as NASA's mission control coordinates spacecraft and satellites, your mission control manages all your [satellites](/docs/terminology.md#satellite) or ([orbiters](#orbiter)). Because it can hold ICP and is only controlled by you, your mission control also functions as your ([wallet](#wallet)). For a schematic representation, refer to the [Architecture](/docs/white-paper/architecture.md) documentation page. ## Modules A module or segment — i.e. Satellite, Mission Control, and Orbiter — refers to a smart contract compiled into WebAssembly (WASM) code and deployed on the Internet Computer with Juno. It acts as a comprehensive entity, encompassing memory, permission checks, and other Juno abstractions. These serve as endpoints that developers and users can query for various functionalities. ![A really high level schema representing a Satellite architecture](/assets/images/satellite-09d7ff40ba0d777f933e75c11863746b.png) ## NFID [NFID](https://nfid.one) is a digital identity that provides private and secure sign-in to applications. It offers a convenient way to authenticate with third-party providers like [Metamask](https://metamask.io/) and [Google](https://www.google.com/account/about/). ## Orbiter An orbiter is the term we use to refer to the smart contract that can optionally be employed for analytics to gather valuable, anonymous insights about your users. ## Principal Principals are generic identifiers for the [console](/docs/terminology.md#console), [mission controls](/docs/terminology.md#mission-control), [satellites](/docs/terminology.md#satellite), and users. They consist of a public-private key pair. When displayed or used as a configuration value, the public ID of the principal is used. Learn more about [principals](https://internetcomputer.org/docs/current/references/ic-interface-spec#principal). ## Satellite A satellite is a container for your application that runs entirely on-chain. It contains your project's data, storage, application bundle, and assets. A satellite is essentially a smart contract with added capabilities. Currently, each satellite is dedicated to a single application. ## Subnet A subnet is like a group of smart contracts (programs) working together on the Internet Computer. These groups, or subnets, are designed to distribute the workload across the network. By having multiple subnets, the Internet Computer can handle more activity, process data faster, and ensure the system remains efficient and secure. When you create a module, like a Satellite, it's deployed on the same subnet as the Juno Console by default: [6pbhf-qzpdk-kuqbr-pklfa-5ehhf-jfjps-zsj6q-57nrl-kzhpd-mu7hc-vae](https://dashboard.internetcomputer.org/subnet/6pbhf-qzpdk-kuqbr-pklfa-5ehhf-jfjps-zsj6q-57nrl-kzhpd-mu7hc-vae). Communicating between modules on different subnets takes longer due to the extra steps required for coordination (about 4 additional consensus rounds in the best case). This is why placing all your interacting smart contracts on the same subnet can lead to significant performance improvements. Think of it like a huge playground with lots of groups of kids playing different games. Each group has its own area to play, and that area is called a subnet. If you want to play with kids in your own group, it’s super fast and easy because you’re all together. But if you want to play with a kid in a different group, it might take a little longer since you have to cross the playground to reach them. By picking the right group (or subnet), everyone can play faster and have more fun. ## Wallet A wallet is your secure repository for managing and storing crypto money. Your wallet is controlled exclusively by you, ensuring that no one, including Juno, can ever access it. Think of it like a digital vault: * It securely stores your ICP tokens. * It acts as your gateway for transactions within the Juno ecosystem. * It allows you to top up your modules with cycles. Because your wallet is also your ([mission-control](#mission-control)) and therefore a smart contract, which requires resources to stay alive, we recommend holding a reasonable amount of tokens. Think of it like a day-to-day wallet for frequent operations rather than one for savings. # Troubleshooting --- ### Windows Powershell If you are using Windows Powershell and encounter the following error after installing the [CLI](/docs/reference/cli.md): > The term 'juno' is not recognized as a name of a cmdlet, function, script file, or executable program. Please ensure that npm is added to your system's PATH (e.g. `C:\Users\{PC_NAME}\AppData\Roaming\npm`). --- ### Windows Not Defined Juno does not support yet Server Side Rendering (see [Roadmap](/docs/white-paper/roadmap.md)). Therefore if you are facing such an issue as `ReferenceError: window is not defined` please make sure that your application is not build using SSR. We generally recommend using Static Site Generation (SSG) / prerendering. --- ### ReferenceError: global is not defined The Juno JavaScript libraries rely on the DFINITY [agent-js](https://github.com/dfinity/agent-js/) libraries to interact with the Internet Computer. These libraries require various Node.js polyfills for the browser, which unfortunately make the bundle heavier. The templates provided by Juno - `npm create juno@latest` - are preconfigured to handle this limitation and issue. However, you might encounter errors if your app bundler is not properly configured. The most common error is global not being available on the client side: ``` ReferenceError: global is not definedat new _Decoder (chunk-3K6K3FD6.js?v=df0b7a78:4968:30)at new Uint8ArrayDecoder (chunk-3K6K3FD6.js?v=df0b7a78:7475:25)at decode2 (chunk-3K6K3FD6.js?v=df0b7a78:7488:19)at _HttpAgent.readState (chunk-3K6K3FD6.js?v=df0b7a78:14483:31)at async chunk-3K6K3FD6.js?v=df0b7a78:15075:26at async Promise.all (:5173/index 0)at async Module.request (chunk-3K6K3FD6.js?v=df0b7a78:15169:3)at async _HttpAgent.syncTime (chunk-3K6K3FD6.js?v=df0b7a78:14532:22)at async Promise.all (:5173/index 0) ``` To resolve this issue, you can configure your bundler to polyfill the required libraries. Since the CLI provides support for most popular frameworks, you might find a proper configuration for your project by referring to the corresponding technology project in this repository: [https://github.com/junobuild/create-juno/tree/main/templates](https://github.com/junobuild/create-juno/tree/main/templates) --- ### ENOENT: no such file or directory When encountering the following error after running `juno deploy`, it is likely caused by either not starting the command from the root directory of your project or having an incorrect configuration for the [source](/docs/build/hosting/configuration.md#source) option, which Juno uses to locate the files for deployment. > An unexpected error happened 😫. Error: ENOENT: no such file or directory, scandir ... Make sure these two requirements are correctly met before restarting the command line. --- ### Canister exceeded its current Wasm memory limit Every Satellite, and generally any module on Juno, starts with a default heap memory limit of 1 GB. While you can increase this limit in the settings, it's not recommended to go beyond it, as it may cause issues when upgrading your module. The heap includes a bit of metadata, any collections you've created in Datastore and Storage (where using stable memory is advised), and the assets of your frontend application. If you're deploying a really large application (>1 GB) or frequently pushing updates to an application that isn’t reproducible, your heap memory usage can grow unexpectedly and eventually hit the limit. When that happens, your next deployment or update might fail to prevent exceeding the limit, which could lead to issues with your module. ``` Request ID: d7be9..bfcb8 Reject code: 5 Reject text: Error from Canister aaaaa-bbbbb-ccccc-ddddd-cai: Canister exceeded its current Wasm memory limit of 1073741824 bytes. The peak Wasm memory usage was 1073872896 bytes. If the canister reaches 4GiB, then it may stop functioning and may become unrecoverable. Please reach out to the canister owner to investigate the reason for the increased memory usage. It might be necessary to move data from the Wasm memory to the stable memory. If such high Wasm memory usage is expected and safe, then the developer can increase the Wasm memory limit in the canister settings..Try checking the canister for a possible memory leak or modifying it to use more stable memory instead of Wasm memory. See documentation: https://internetcomputer.org/docs/current/references/execution-errors#wasm-memory-limit-exceeded ``` #### Preventing Heap Memory Issues To avoid running into memory limits, it's important to monitor memory usage and follow two key best practices: ##### Use Stable Memory for Datastore and Storage Stable memory is designed for long-term storage and helps prevent heap memory from growing uncontrollably. Whenever possible, store large datasets in stable memory instead of the heap. ##### Ensure Your Frontend Build is Reproducible When building your frontend (e.g. with `npm run build`), the output should be identical to the previous build if no changes were made. Why does this help? When you deploy your application, Juno does not clear existing files—it only adds new ones. To optimize this process, Juno compares the names and content (hash values) of all files with those already uploaded. If a file hasn't changed, it is skipped, reducing unnecessary memory usage and saving cycles. If your build output isn’t reproducible, every deployment could introduce slightly different files, even if nothing has changed in your code. Over time, this would lead to unnecessary file accumulation, increasing heap memory usage and eventually causing issues. ##### Resolving Heap Memory Issues There are different ways to resolve this issue, and the best approach depends on the features you're using. If you're using Datastore and Storage, we need to find a solution that prevents data loss. If you're only hosting a website, the steps to fix the issue will be much simpler. In any case, the best course of action is to reach out so we can assess your situation and find a tailored solution together. # Development Learn how to track page views, custom events, and performance metrics. --- ## Page views Page views, such as when a visitor opens your website or navigates to a subpage, are automatically tracked once you have configured, initialized, and deployed your application with the analytics module. There's **no need** for additional development work! However, if you (really) want to trigger page view tracking manually, you can do so using the `trackPageView()` function provided by the SDK. ``` import { trackPageView, trackPageViewAsync } from "@junobuild/analytics";trackPageView(); // or await trackPageViewAsync(); ``` --- ## Track custom events Custom events can be tracked using the `trackEvent` function. You need to provide a `name` for the event, and you can include up to 10 custom `metadata` fields. **Note:** This is an option. As explained in the previous chapter, the library will take care of gathering insightful anonymous data as soon as it is configured and initialized. Custom events are useful if you want to take an extra step and collect your own specific information. Here's an example of how to use it: ``` import { trackEvent, trackEventAsync } from "@junobuild/analytics";// Fire-and-forgettrackEvent({ name: "Your custom event", metadata: { your_key: "A value", your_other_key: "Another value" }});// Or await it if neededawait trackEvent({ name: "Your custom event", metadata: { your_key: "A value", your_other_key: "Another value" }}); ``` Use the `async` version if you're tracking events for which you want to absolutely ensure delivery before continuing the flow — for example, before navigating away or submitting critical user input. That said, the tracker sends data using `keepalive` fetch requests by default, so in most cases there’s no difference in reliability — the choice is mostly a matter of convenience and flow control. **Important:** For scalability and optimization reasons, the data collected must adhere to certain rules, particularly regarding their length. For instance, a randomly generated key should not exceed 36 bytes in length. For detailed information about these rules, please refer to Juno's GitHub [repository](https://github.com/junobuild/juno). --- ## Campaign tracking with UTM parameters Juno Analytics automatically supports [UTM parameters](https://en.wikipedia.org/wiki/UTM_parameters) out of the box. These are standard query parameters (like `utm_source`, `utm_medium`, and `utm_campaign`) commonly added to links in newsletters, ads, and social posts to help you understand how visitors reach your app. They're added to the end of a URL as query parameters. For example: ``` ?utm_source=newsletter&utm_medium=email&utm_campaign=rocket-launch ``` As long as your URLs include UTM tags, campaign data will be collected and shown in your dashboard — no additional setup needed. ### Common UTM parameters | Parameter | Required | Description | Example | | --- | --- | --- | --- | | `utm_source` | ✅ | Where the traffic comes from | `newsletter`, `twitter`, `github` | | `utm_medium` | | The channel used | `email`, `social` | | `utm_campaign` | | The name of the campaign | `rocket-launch` | | `utm_term` | | Keywords for paid search | `juno+analytics` | | `utm_content` | | Distinguish between different links | `header-button`, `footer-link` | Only the `utm_source` field is mandatory. If it's missing, the campaign will not be tracked. # Setup This section covers how to integrate and configure Juno Analytics in your app or website. --- ## Getting Started Before integrating Juno Analytics into your app or website, you need to create an Orbiter - the smart contract that implements analytics features and gathers data. Here's a step-by-step guide to help you get started: 1. Sign in to the Juno [Console](https://console.juno.build) 2. Navigate to [Analytics](https://console.juno.build/analytics/) 3. Click on **Get started** 4. Confirm by selecting **Create analytics** 5. (Optional) In **Advanced Options**, choose a European subnet if you want your data stored in Europe 6. Once the setup completes, click **Close** to exit the wizard 🎉 You've now created your Analytics Orbiter! But you're not done yet — you still need to tell it which Satellites (apps) can send data. 🛠 **Final Step: Setup Tracking** Go to the [Setup](https://console.juno.build/analytics/?tab=setup) tab in the Analytics page and select which Satellites should be allowed to track page views and events. --- ## Setup There are two ways to integrate Juno Analytics into your project: 1. Using your favorite package manager (`npm`, `yarn`, `pnpm`). ([Learn how](#1-with-package-manager)). 2. Without installation by fetching the library from a CDN. ([Learn how](#2-from-a-cdn)). --- ### 1\. With Package Manager Follow these steps to install and initialize the SDK using your preferred package manager. #### Install the Library To install the analytics library, run the following command: * npm * yarn * pnpm ``` npm i @junobuild/analytics ``` ``` yarn add @junobuild/analytics ``` ``` pnpm add @junobuild/analytics ``` #### Configure If you're using the [Next.js](/docs/reference/plugins.md#nextjs-plugin) or [Vite](/docs/reference/plugins.md#vite-plugin), you can define your configuration in your `juno.config` file. juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist" }, orbiter: { id: "aaaa-bbbbb-ccccc-ddddd-cai" // <-- Replace with your ID }}); ``` If you're not using a plugin, you can skip this step and instead provide the IDs manually when initializing the Orbiter (see next section). #### Initialize ✅ Using plugins and config Just call `initOrbiter()` as early as possible in your app startup: ``` import { initOrbiter } from "@junobuild/analytics";initOrbiter(); ``` 🛠 Without plugins Pass your Satellite and Orbiter IDs manually: ``` import { initOrbiter } from "@junobuild/analytics";initOrbiter({ satelliteId: "", // replace with your Satellite ID orbiterId: "" // replace with your Orbiter ID}); ``` --- ### 2\. From a CDN If you don't want to - or cannot - install anything locally, you can load the SDK directly from a CDN. Add the following script to your HTML (for example, in `index.html`). This will fetch the library from [jsDelivr](https://www.jsdelivr.com/) and start the analytics when someone loads your site: ``` ``` --- ## Optional Features The SDK includes a few optional features you can enable to enrich your analytics. By default, these are disabled to keep your bundle small and your app fast. --- ### UA Parser By default, the library uses a naive approach to analyze the user agent string — enough to detect general categories (like mobile vs desktop) — while keeping the bundle lean and fast. If you need more detailed insights such as browser name, OS, or device model, you can opt in to use a full UA parser. #### What It Adds When enabled, the parser collects: * Browser name and version * Operating system * Device type (e.g., mobile, desktop, tablet) These enrich the stats visible in the dashboard, including OS and better browser breakdowns. #### Why It's Opt-In * Adds a few extra kilobytes to the app bundle * Disabled by default to preserve performance and minimize boot time **Note:** A more complete UA parsing approach could be performed inside a container, but this would currently require too many resources impacting both performance and cost. Delegating it to the frontend keeps things fast and efficient. #### How to Enable Pass `userAgentParser: true` when calling `initOrbiter()`: ``` import { initOrbiter } from "@junobuild/analytics";initOrbiter({ options: { userAgentParser: true }}); ``` --- ### Performance Metrics Juno Analytics supports tracking key performance metrics using [Web Vitals](https://github.com/GoogleChrome/web-vitals). This feature is **opt-in** and requires configuration in both the Console and your app's code. #### Key Metrics When enabled, the following Web Vitals are tracked: * **Time to First Byte (TTFB)**: Measures the time it takes for the first byte of data to reach the user's browser, indicating server responsiveness. * **First Contentful Paint (FCP)**: Marks the time when the first piece of content is rendered, helping assess initial loading speed. * **Largest Contentful Paint (LCP)**: Tracks the time when the largest content element becomes visible, indicating when the main content is likely fully loaded. * **Cumulative Layout Shift (CLS)**: Quantifies unexpected layout shifts during loading, reflecting visual stability. * **Interaction to Next Paint (INP)**: Measures the latency of interactions, such as clicks, to evaluate application responsiveness. #### How to Enable Web Vitals To start collecting performance metrics, you need to enable it in two places: 1. **In the Console** Go to your Orbiter's [Setup tab](https://console.juno.build/analytics/?tab=setup) and click "Edit Configuration". Enable the "Web Vitals" option under the "Advance Options" to allow the Orbiter to store performance data. 2. **In your App** Enable Web Vitals in the SDK during initialization. This ensures that the additional logic is only loaded when needed, helping keep your app's initial load size minimal. ``` import { initOrbiter } from "@junobuild/analytics";initOrbiter({ options: { performance: true }}); ``` --- ## Best Practices Here are some useful tips for working with the analytics. ### Production vs Development While the example above shows analytics being initialized in all cases, it's recommended to **disable analytics during local development**. This prevents test data from polluting your metrics if your local environment is connected to production, and avoids errors when analytics aren't set up locally which is often the case during development. ``` if (DEV) { return;}initOrbiter(); ``` ### Use Environment-Specific IDs You can also configure different IDs for different environments (e.g., development and production): juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist" }, orbiter: { ids: { production: "aaaa-bbbbb-ccccc-ddddd-cai", development: "ffff-eeee-ddddd-ccccc-cai" } }}); ``` # Customization Here are some customization options to tailor your sign-in flow and handle session expiration. --- ## Sign-In Providers Juno supports Internet Identity and NFID, which also offers additional authentication methods like Google and email. **Note:** You can implement the `signIn` function in your application as many times as you wish, with various configurations. It is also perfectly acceptable to use both Internet Identity and NFID within the same project. --- ### Internet Identity Internet Identity is available at two different URLs: `internetcomputer.org` and `ic0.app`. By default, the SDK uses `internetcomputer.org`. ``` import { signIn, InternetIdentityProvider } from "@junobuild/core";// Default domain is 'internetcomputer.org'await signIn({ provider: new InternetIdentityProvider({})}); ``` You can switch to `ic0.app` by setting the domain option accordingly. ``` import { signIn, InternetIdentityProvider } from "@junobuild/core";await signIn({ provider: new InternetIdentityProvider({ domain: "ic0.app" })}); ``` We use the former by default because we believe it offers a better user experience and branding. **Note:** It is worth mentioning that your users will be able to sign in to your app with Internet Identity, regardless of which of those two domains they originally created their identity on. --- ### NFID To set up NFID, you need to configure the corresponding provider and provide your application name and a link to your logo. ``` import { signIn, NFIDProvider } from "@junobuild/core";await signIn({ provider: new NFIDProvider({ appName: "Your app name", logoUrl: "https://somewhere.com/your_logo.png" })}); ``` --- ## Session Expiration To proactively detect when a session duration expires, you can use the pre-bundled Web Worker provided by Juno's SDK. To do so, you can follow these steps: 1. Copy the worker file provided by Juno's SDK to your app's static folder. For example, to your `public` folder with a NPM `postinstall` script: ``` { "scripts": { "postinstall": "node -e \"require('fs').cpSync('node_modules/@junobuild/core/dist/workers/', './static/workers', {recursive: true});\"" }} ``` Once configured, run `npm run postinstall` manually to trigger the initial copy. Every time you run `npm ci`, the post-install target will execute, ensuring the worker is copied. 2. Enable the option when you initialize Juno: ``` import { initSatellite } from "@junobuild/core";await initSatellite({ workers: { auth: true }}); ``` The `auth` option can accept either `true`, which will default to using a worker located at [https://yourapp/workers/auth.worker.js](https://yourapp/workers/auth.worker.js), or a custom `string` to provide your own URL. When the session expires, it will automatically be terminated with a standard [sign-out](/docs/build/authentication/development.md#sign-out). Additionally, an event called `junoSignOutAuthTimer` will be thrown at the `document` level. This event can be used, for example, to display a warning to your users or if you wish to reload the window. ``` document.addEventListener( "junoSignOutAuthTimer", () => { // Display an information to your users }, { passive: true }); ``` The worker also emits an event named `junoDelegationRemainingTime`, which provides the remaining duration in milliseconds of the authentication delegation. This can be useful if you want to display to your users how much time remains in their active session. ``` document.addEventListener( "junoDelegationRemainingTime", ({ detail: remainingTime }) => { // Display the remaining session duration to your users }, { passive: true }); ``` # Development This page provides an overview of how to integrate authentication features with the Juno SDK, including sign-in, sign-out, and user session subscription within your app. **Note:** The Juno SDK must be [installed](/docs/setup-the-sdk.md) and initialized in your app to use the authentication features. --- ## Sign-in You can authorize an existing or new user with the identity provider using `signIn`. ``` import { signIn } from "@junobuild/core";await signIn(); ``` The sign-in feature supports following customization options: | Option | Default Value | Description | | --- | --- | --- | | `maxTimeToLive` | `BigInt(4 * 60 * 60 * 1000 * 1000 * 1000)` | Specifies the duration for the session (defaults to **4 hours**). It's **important** to note that this duration remains constant, whether the users are active or inactive. | | `windowed` | `true` | By default, the authentication flow is presented in a popup window on desktop that is automatically centered on the browser. This behavior can be turned off by setting the option to `false`, causing the authentication flow to happen in a separate tab instead. | | `derivationOrigin` | — | The main domain to be used to ensure your users are identified with the same public ID, regardless of which of your satellite’s URLs they use to access your application. | | `allowPin` | `false` | We consider the specific PIN authentication method of [Internet Identity](https://internetcomputer.org/docs/current/references/ii-spec#client-authentication-protocol) as "insecure" because users can easily lose their login information if they do not register a passphrase, particularly as Safari clears the browser cache every two weeks in cases of inactivity. This is why we **disable** it by default. | You can configure the default sign-in flow that uses Internet Identity. You can also set NFID as a provider. Check out the [advanced Sign-in guidelines](/docs/build/authentication/customization.md#sign-in-providers) for more details. ### Handling Errors If the sign-in flow encounters an error, an exception will be thrown. When a user cancels sign-in (e.g., by closing the modal), the library throws a `SignInUserInterruptError`. This error indicates that the user intentionally interrupted the sign-in process, and it's generally best practice to ignore it rather than showing an error message. ``` import { signIn } from "@junobuild/core";try { await signIn();} catch (error: unknown) { if (error instanceof SignInUserInterruptError) { // User canceled sign-in, no need to show an error return; } // Handle other errors console.error("Sign-in failed:", error);} ``` --- ## Sign-out You can end a user's session by logging them out. ``` import { signOut } from "@junobuild/core";await signOut(); ``` **Note:** This will clear the sign-in information stored in IndexedDB. --- ## Subscription You can subscribe to the user state using the subscriber function. This function provides a technical user and triggers whenever the user's state changes. In other words, using this callback allows you to monitor whether the user is signed in or signed out. ``` import { authSubscribe } from "@junobuild/core";authSubscribe((user: User | null) => { console.log("User:", user);}); ``` If you register the subscriber at the top of your application, it will propagate the user's state accordingly (e.g. `null` when a new user opens the app, the new user's entry when they sign in, the existing user when they refresh the browser within the valid duration, and `null` again when they sign out). Subscribing returns a callback that can be executed to unsubscribe: ``` import { authSubscribe } from "@junobuild/core";const unsubscribe = authSubscribe((user: User | null) => { console.log("User:", user);});// Above subscriber ends nowunsubscribe(); ``` # Management This page provides an overview of the administrative functions available in the Juno Console related to user management. --- ## Banning Users The built-in authentication feature allows developers to ban or unban users within their dapps. When a user is banned, they lose access to key services such as Datastore and Storage, preventing them from creating, updating, or deleting any data. This feature helps developers prevent misuse, spam, or abusive behavior in their applications. **Note:** A ban is not a deletion. The user’s authentication entry remains in the system, and they can be unbanned at any time. ### How to Ban a User To ban a user, follow these steps: * Navigate to the Authentication section in the [console](https://console.juno.build). * Find the user you want to ban in the users' table. * Click on the Active / Ban button at the start of the row. * Confirm the action. Once banned, the user will not be able to sign in, create, update, or delete data in Datastore or Storage. ![A screenshot of the Juno Console's Authentication section, displaying the user management interface with options to ban or unban users](/assets/images/user-management-ban-90535848bf31b97659d5fecd4e4d54f1.webp) # Collections You can create or update a collection in the "Collections" tab in Juno's console under the [datastore](https://console.juno.build/datastore) view. --- ## Configuration Each collection has a set of configurable options that define its behavior and limitations: | Option | Mandatory | Description | | --- | --- | --- | | Key | Yes | A unique identifier for the collection. The key that you will use in your code to interact with a particular collection. | | Read permission | Yes | Defines who can read documents in the collection. See ([Permissions](#permissions)) below. | | Write permission | Yes | Defines who can create, update, or delete documents. See ([Permissions](#permissions)) below. | | Memory | Yes | Specifies whether the collection uses `heap` or `stable` memory. This setting is permanent and cannot be changed after creation. The default is `stable` memory. For more information, see the related [documentation](/docs/miscellaneous/memory.md). | | Max changes per user | No | Limits the number of documents a single user can create, update, or delete in the collection. This helps maintain fair resource distribution across users. | | Max capacity | No | The maximum number of documents that can be stored in the collection, applying to the entire collection regardless of individual users. | | Max updates per minute | No | Limits the number of creation, update and delete operations per minute to prevent excessive updates. | | Immutable permissions | No | If enabled, read and write permissions cannot be modified after creation. | --- ## Permissions Permissions define who can read and write documents in a collection. Writing includes creating, updating, and deleting documents. | Permission | Description | | --- | --- | | **Public** | Anyone can read or write documents in the collection. | | **Private** | Only the creator (owner) of a document can read or write to it. However, note that since satellite administrators manage the underlying infrastructure, they have the technical ability to modify access rules by changing its source code. | | **Managed** | The owner of a document, the administrator and editor of the satellite can read or write to it in the collection. | | **Restricted** | Only satellite administrator and editor can read or write any document in the collection. | If not set to immutable, you can modify the permissions at any time, and the changes will take effect immediately. **Tip:** Any collection with read permissions set to `public`, `managed` or `restricted` will allow the developer to view its content in the console under the [datastore](https://console.juno.build/datastore) view. # Development This page provides an overview of how to integrate and manage documents using the Juno SDK, including adding, retrieving, updating, listing, and deleting documents within your app. **Important:** The functions described on this page are intended for use in the **client-side** of your app. If you're looking to extend backend capabilities using serverless logic, refer to the [Functions documentation](/docs/build/functions.md). --- ## Add a document To add a document, use the `setDoc` function: ``` import { setDoc } from "@junobuild/core";await setDoc({ collection: "my_collection_key", doc: { key: "my_document_key", data: myExample }}); ``` You need to provide the `collection` in which to save the data and the `key` to use as an index for the document. The `data` can be any [JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description)\-serializable data. ### Key The `key` can be any `string`, but it's recommended to generate IDs using the [nanoid](https://github.com/ai/nanoid) library. ``` import { setDoc } from "@junobuild/core";import { nanoid } from "nanoid";const myId = nanoid();await setDoc({ collection: "my_collection_key", doc: { key: myId, data: myExample }}); ``` ### Description A document can be saved with an optional `description` field, allowing for a maximum length of 1024 characters. This field serves both descriptive purposes and can be used for more granular filtering of your documentation. When retrieving documents, you can also filter based on the description field in addition to the keys, providing additional flexibility and organization options. ``` import { setDoc } from "@junobuild/core";await setDoc({ collection: "my_collection_key", doc: { key: "my_document_key_1", data: myExample, description: "This is a description" }});await setDoc({ collection: "my_collection_key", doc: { key: "my_document_key_2", data: myExample, description: "#programming #technology #web3 #junobuild" }}); ``` --- ## Get a document To retrieve data, use the `getDoc` function and provide the `collection` and the `key` of the document: ``` import { getDoc } from "@junobuild/core";const myDoc = await getDoc({ collection: "my_collection_key", key: myId}); ``` --- ## Get multiple documents Obtaining multiple documents at once can improve performance compared to making multiple individual `getDoc` calls depending on the use case. You can achieve this by using the `getManyDocs` function: ``` import { getManyDocs } from "@junobuild/core";const docPair1 = { collection: "my_collection", key: "my_document_key_1"};const docPair2 = { collection: "my_other_collection", key: "my_document_key_2"};const docs = await getManyDocs({ docs: [docPair1, docPair2] }); ``` --- ## Update a document To update a document, use the `setDoc` function with its current version to validate that the most recent entry is being updated: ``` import { setDoc } from "@junobuild/core";await setDoc({ collection: "my_collection_key", doc: { key: myId, data: myExample, version: 3n }}); ``` The `version` must match the current version of the last document within the satellite; otherwise, the call will fail. This prevents unexpected concurrent overwrites, which is useful, for example, if your users use your projects simultaneously on multiple devices. **Tip:** You can spread the document you have previously retrieved, for example with `getDoc`, to populate the `version` and `key` fields. ``` import { setDoc } from "@junobuild/core";await setDoc({ collection: "my_collection_key", doc: { ...myDoc, // includes 'key' and 'version' data: myNewData }}); ``` --- ## Set multiple documents You might need to set multiple documents, whether within the same collection or across collections, all at once in an atomic manner. This ensures that if any of the creations or deletions fail, the entire batch will be automatically reverted. You can achieve this using the `setManyDocs` function: ``` import { setManyDocs } from "@junobuild/core";const update1 = { collection: "my_collection", doc: { key: "my_document_key_1", data: { hello: "world" } }};const update2 = { collection: "my_other_collection", doc: { key: "my_document_key_2", data: { count: 123 } }};const docs = await setManyDocs({ docs: [update1, update2] }); ``` --- ## List documents The `listDocs` function is used to retrieve documents from a specified collection. ``` import { listDocs } from "@junobuild/core";const myList = await listDocs({ collection: "my_collection_key"}); ``` ### Parameters The function requires a collection and accepts various optional parameters, including a matcher (a regex applied to the document keys and descriptions), pagination options, and sorting order. 1. **`collection`** (required) * **Description**: The key of the collection from which documents are to be listed. * **Type**: `string` 2. **`filter`** (optional) * **Description**: An optional object that can be used to provide various parameters to filter documents. a. **`matcher`** (optional) * **Description**: An object used to filter documents based on their keys or descriptions using regular expressions. * **Type**: `ListMatcher` ``` interface ListMatcher { key?: string; description?: string; createdAt?: ListTimestampMatcher; updatedAt?: ListTimestampMatcher;} ``` * **key**: A regex to match against document keys. * **description**: A regex to match against document descriptions. * **createdAt**: A `ListTimestampMatcher` to filter documents based on their creation timestamp. * **updatedAt**: A `ListTimestampMatcher` to filter documents based on their last update timestamp. * **Type**: `ListTimestampMatcher` can be used to specify criteria for timestamp matching. ``` type ListTimestampMatcher = | { matcher: "equal"; timestamp: bigint; } | { matcher: "greaterThan"; timestamp: bigint; } | { matcher: "lessThan"; timestamp: bigint; } | { matcher: "between"; timestamps: { start: bigint; end: bigint; }; }; ``` * **matcher**: Specifies the type of timestamp comparison. Can be one of the following: * **equal**: Matches documents where the timestamp is exactly equal to the specified value. * **greaterThan**: Matches documents where the timestamp is greater than the specified value. * **lessThan**: Matches documents where the timestamp is less than the specified value. * **between**: Matches documents where the timestamp falls within a specified range. * **timestamp**: Used with `equal`, `greaterThan`, and `lessThan` matchers to specify the exact timestamp for comparison. * **timestamps**: Used with the `between` matcher to specify a range of timestamps. The range is inclusive of both the start and end values. b. **`paginate`** (optional) * **Description**: An object to control pagination of the results * **Type**: `ListPaginate` ``` interface ListPaginate { startAfter?: string; limit?: number;} ``` * **startAfter**: A string key to start listing documents after this key. * **limit**: The maximum number of documents to return. c. **`order`** (optional) * **Description**: Control the sorting order of the results. * **Type**: `ListOrder` ``` interface ListOrder { desc: boolean; field: ListOrderField;}type ListOrderField = "keys" | "updated_at" | "created_at"; ``` d. **`owner`** (optional) * **Description**: The owner of the documents. * **Type**: `ListOwner` ``` type ListOwner = string | Principal; ``` **Example:** Usage of the parameters: ``` import { listDocs } from "@junobuild/core";const myList = await listDocs({ collection: "my_collection_key", filter: { matcher: { key: "^doc_", description: "example", createdAt: { matcher: "greaterThan", timestamp: 1627776000n }, updatedAt: { matcher: "between", timestamps: { start: 1627770000n, end: 1627900000n } } }, paginate: { startAfter: "doc_10", limit: 5 }, order: { desc: true, field: "updated_at" }, owner: "some_owner_id_or_principal" }}); ``` The function returns the documents and various information, in the form of an object whose interface is given below. ``` { items: []; // The data - array of documents items_length: bigint; // The number of documents - basically items.length items_page?: bigint; // If the query is paginated, at what page (starting from 0) do the items find the place matches_length: bigint; // The total number of matching results matches_pages?: bigint; // If the query is paginated, the total number (starting from 0) of pages} ``` --- ## Count documents The `countDocs` function is used to count the number of documents in a specified collection without retrieving the actual documents. ``` import { countDocs } from "@junobuild/core";const count = await countDocs({ collection: "my_collection_key"}); ``` ### Usage This function accepts similar parameters as the `listDocs` function, including `collection`, `matcher`, and `owner`, and returns the count of matching documents. For detailed information on how to use these parameters, refer to the ([List documents](#list-documents)) section. The return value is the same as the `items_length` property from the `listDocs` function, providing the count of documents that match the criteria. --- ## Delete There are multiple ways to delete documents from your Datastore. ### Delete a document To delete a document, use the `deleteDoc` function, which performs version validation to ensure that the most recent document is being deleted: ``` import { deleteDoc } from "@junobuild/core";await deleteDoc({ collection: "my_collection_key", doc: myDoc}); ``` The document must include the current `version` from the latest entry within the satellite; otherwise, the call will fail. This prevents unexpected concurrent overwrites, which is particularly useful if your users access your projects simultaneously on multiple devices. ### Delete multiple documents To delete multiple documents in an atomic manner, you can use the function `deleteManyDocs`: ``` import { deleteManyDocs } from "@junobuild/core";await deleteManyDocs({ docs: [myDoc1, myDo2, myDoc3] }); ``` ### Delete filtered documents The `deleteFilteredDocs` function allows you to delete multiple documents from a collection based on specific filter criteria. This function simplifies bulk deletions by leveraging the same parameters as the ([listDocs](#list-documents)) function for filtering. ``` import { deleteFilteredDocs } from "@junobuild/core";await deleteFilteredDocs({ collection: "my_collection_key", filter: { // Same options as filter of listDocs }}); ``` # Lifecycle Understand the full journey of Serverless Functions in Juno, from setup and development to deployment and maintenance. --- ## Initial Setup If you didn’t use a template or skipped the language selection during setup, you can run `juno functions eject` at the root of your project. This command configures your project with the appropriate setup based on your language of choice. For Rust, it includes a `Cargo.toml` and a `lib.rs` file. For TypeScript, it sets up an `index.ts` file. --- ## Developing Functions Once your project is scaffolded, you can start writing your functions. Use the CLI to build them with: ``` juno functions build ``` If you start the emulator (see chapter below) in watch mode, your functions will be rebuilt automatically on save, so you don’t need to run this command manually. --- ## Local Development For local development and testing, a sandbox environment is essential. You can establish this environment by running the CLI command `juno dev start`. **Info:** Find more information about local development in the [documentation](/docs/guides/local-development.md). The local sandbox environment supports hot reloading. This means that the container will automatically redeploy your local Satellite each time `juno dev build` is executed and a new version is produced. If you start the emulator with the `--watch` flag, it will also rebuild your functions automatically when changes are detected. --- ## Deploying Juno offers two main ways to deploy your serverless functions, depending on whether you're working locally or integrating with a CI pipeline like GitHub Actions. --- ### 1\. Direct Deploy If you're developing locally or in another environment where the **access key has admin privileges**, you can deploy the latest build directly: ``` juno functions upgrade ``` * ✅ Skips the CDN. * ✅ Immediate deployment. * 🔐 Requires access key with upgrade permission. * 📦 Uses the default path: `./target/deploy/satellite.wasm.gz`. Optional: ``` juno functions upgrade --src ./path/to/custom-build.wasm.gz ``` --- ### 2\. With CDN + Approval Workflow If you're using CI (like GitHub Actions) or an environment where your **access key has write privileges**: #### a) Publish to CDN In your GitHub Action or script: ``` juno functions publish ``` With options: ``` juno functions publish --mode staging --src ./path/to/build.wasm.gz ``` * 📤 Uploads to the Satellite’s CDN release. * 🔐 Requires access key with **write** role. #### b) Upgrade from CDN From your local CLI or in the Console UI, you can then upgrade. ``` juno functions upgrade --cdn ``` * 🔎 Interactively selects the WASM version from the published CDN. * 🧾 Only deploys with a key that has **upgrade** rights. * 🕵️ If your key is **submit-only**, the change will wait for approval in the Console UI or CLI. --- ### 3\. Optional Approval Flow If you're using CI (like GitHub Actions) or an environment where your **access key has submit privileges** — meaning it cannot directly modify or write data — you can follow a workflow that requires manual approval before deployment. ``` juno functions publish --no-apply ``` Then the published release: * Becomes a **pending change**. * Must be **reviewed and applied** manually. Apply the change either in the Console UI or directly using the CLI. #### 📜 List submitted changes ``` juno changes list ``` #### ✅ Apply a specific change ``` juno changes apply --id ``` Once applied, the Satellite can then be upgraded in the CLI or respectively in the Console UI using: ``` juno functions upgrade --cdn-path ``` --- ## Summary A quick reference for the most common CLI commands and deployment workflows when working with serverless functions in Juno. ### 🛠️ Common CLI Commands | Command(s) | Scenario | | --- | --- | | `juno functions eject` | Initializes your project for writing serverless functions. | | `juno dev start` | Starts the emulator. | | `juno functions build` | Compiles your custom Satellite's code. Changes are automatically redeployed locally. | ### 🚀 Deployment Scenarios | Command(s) | Scenario | | --- | --- | | `juno functions upgrade` | Upgrades your serverless functions immediatly. | | `juno functions upgrade --src ./path/to/custom-build.wasm.gz` | Use a custom WASM path to upgrade. | | `juno functions publish` in CI → `juno functions upgrade --cdn` in CLI or Console UI | CI/CD with write access. | | `juno functions publish --no-apply` in CI → `juno changes apply` → `juno functions upgrade --cdn-path ...` | CI/CD with submit-only access. | | `juno functions publish --mode staging` | CI/CD using staging environment. | # Logs Writing and viewing logs is a crucial tool for debugging and monitoring your code. Serverless Functions offer you the option to utilize loggers to report status effectively. ![A screenshot of the Juno's Console feature to browse logs](/assets/images/functions-754bffa459e2345077924846bf09ef3d.webp) --- ## Native Logging The Internet Computer provides a native, simple logging system where logs are persisted within the blockchain up to a maximum size of 4 KiB. **Note:** If new logs exceed the 4 KiB limit, the oldest entries will be removed. Logs persist across Satellite upgrades. These logs are the preferred method for writing and viewing logs, as they are more efficient and cost-effective compared to the custom solution below. They also persist regardless of whether the update call succeeds or fails. However, native logs do not yet support log levels, that's why all entries will appear as "Error" in the Juno Console. Any printed or trapped messages using the `ic_cdk` crate are automatically collected as logs. ### Example Usage ``` fn log() { ic_cdk::print("This is a log entry."); ic_cdk::trap("There was an error.");} ``` --- ## Custom Logging Custom logging provides a flexible way to track and debug function executions within your Satellite if the native logging does not answer your needs. ### How does it work? Logs are stored in stable memory, accommodating up to 100 entries. Once this limit is reached, the oldest entry is discarded. It's important to note that since logs are saved in memory, your hooks should return a success—meaning they should not trap—otherwise, the information cannot be preserved. ### Available loggers | Logger | Level | Description | | --- | --- | --- | | `log` | Info | Logs a message. | | `log_with_data` | Info | Logs a message with additional serialized data. | | `info` | Info | Logs an informational message. | | `info_with_data` | Info | Logs an informational message with additional serialized data. | | `debug` | Debug | Logs an debug-level message. | | `debug_with_data` | Debug | Logs a debug-level message with additional serialized data. | | `warn` | Warning | Logs a warning message. | | `warn_with_data` | Warning | Logs a warning message with additional serialized data. | | `error` | Error | Logs an error message. | | `error_with_data` | Error | Logs an error message with additional serialized data. | # Rust This page covers advanced options for writing serverless functions in Rust. --- ## Including the Satellite After defining your Functions, at the very end of your `lib.rs` module, include the Satellite to ensure that your custom logic and the default features or Juno are properly registered and executable within the Juno ecosystem. **Important:** This is crucial for compatibility with the Juno Console and CLI, as it expects the Satellite to expose the necessary functionality for monitoring, deployment, and interaction. Without this macro, certain features in the Console may not function correctly. ``` include_satellite!(); ``` --- ## Feature Selection When you run `juno dev eject`, all the available hooks and assertions are scaffolded in your `lib.rs` module. However, if you don’t have to implement all of them for example to improve readability or reduce unnecessary logic, you can selectively enable only the features you need. To do this, disable the default features in your `Cargo.toml` and explicitly specify only the ones you want to use. For example, if you only need `on_set_doc` and `assert_set_doc`, configure your `Cargo.toml` like this: ``` [dependencies]junobuild-satellite = { version = "0.0.21", default-features = false, features = ["on_set_doc", "assert_set_doc"] } ``` With this setup, only `on_set_doc` and `assert_set_doc` must be implemented with custom logic, while other hooks and assertions will not be included in your Satellite. --- ## Maintenance After deployment, keeping your Satellite functional and optimized requires ongoing monitoring and updates. Staying up to date is also a key factor, as we may introduce new features that need to be integrated into your Satellite to ensure full functionality within the Juno Console. Since your project includes all Satellite features using `include_satellite!();`, it's essential to stay in sync with Juno’s updates to maintain compatibility. **Caution:** Always upgrade iteratively and avoid skipping version numbers. While we strive to minimize breaking changes, it's crucial to upgrade through each released version sequentially. For example, if you're on **v0.0.23** and the latest release is **v0.0.26**, first upgrade to **v0.0.24**, then **v0.0.25**, and finally **v0.0.26**. Skipping versions could lead to unexpected issues. ### Updating Your Satellite To upgrade your Satellite, bump the dependencies in your `Cargo.toml` file located in `/src/satellite/`. The key dependencies to check and update are: * `junobuild-satellite` * `junobuild-storage` * `junobuild-macros` * `ic_cdk` * `candid` If other crates in your project depend on these, they should also be upgraded accordingly. The recommended versions for each release can be found in the [changelog](/changelog) or [release notes on GitHub](https://github.com/junobuild/juno/releases). If you need assistance, feel free to reach out through the available support channels. --- ## Versioning Every Satellite that runs your serverless functions includes a version number, defined in the `Cargo.toml` file of your project: ``` [package]name = "satellite"version = "0.1.0" ``` This version is embedded into the compiled Wasm binary and displayed in the Juno Console. It helps you: * Track which version of your serverless logic is currently deployed. * Debug more effectively by matching behavior in the Console with specific code versions. * Move independently of Juno updates — you're in full control of your own function versioning. You can use any versioning scheme that suits your development workflow (e.g. `0.1.0`, `1.0.0-beta`, `2025.04.18`...). --- ## Additional Notes WebAssembly (Wasm) binaries serve as the compilation target for the Satellites. While Juno's CLI automatically specifies this target for you, manual execution of certain `cargo` commands necessitates explicitly providing this target. For instance: ``` cargo clippy --target=wasm32-unknown-unknown ``` # TypeScript This page covers advanced options for writing serverless functions in TypeScript. --- ## Maintenance Since your project includes all Satellite features, it's essential to stay in sync with Juno’s updates to maintain compatibility. Always check the [releases](https://github.com/junobuild/juno/releases) page for the latest changes, and update your local container image (source [repo](https://github.com/junobuild/juno-docker)) accordingly to ensure you're running the latest runtime and features. **Caution:** Always upgrade iteratively and avoid skipping version numbers. While we strive to minimize breaking changes, it's crucial to upgrade through each released version sequentially. For example, if you're on **v0.0.23** and the latest release is **v0.0.26**, first upgrade to **v0.0.24**, then **v0.0.25**, and finally **v0.0.26**. Skipping versions could lead to unexpected issues. --- ## Versioning When writing serverless functions in TypeScript, Juno uses the version defined in your project’s `package.json`. This version is embedded into the compiled Wasm module and shown in the Juno Console, making it easier to keep track of deployments. By default, the version is inherited from the root-level `version` field: ``` { "name": "demo", "version": "0.0.10"} ``` However, if you want to version your functions independently from your app or workspace, you can define a custom version field specifically for Juno Functions: ``` { "name": "demo", "version": "0.0.10", "juno": { "functions": { "version": "0.0.4" } }} ``` This version is embedded into the compiled Wasm binary and displayed in the Juno Console. It helps you: * Track which version of your serverless logic is currently deployed. * Debug more effectively by matching behavior in the Console with specific code versions. * Move independently of Juno updates — you're in full control of your own function versioning. You can use any versioning scheme that suits your development workflow (e.g. `0.1.0`, `1.0.0-beta`, `2025.04.18`...). # Configuration You can customize your hosting environment to fit your needs, including: * Specify which `source` files in your local project directory you want to deploy? ([Learn how.](#source)) * Ignore some files during deployment. ([Learn how.](#ignore-files)) * Configure HTTP `headers` to pass along additional information about a request or a response. ([Learn how.](#http-headers)) * Serve a customized 404 page. ([Learn how.](#customize-a-404not-found-page)) * Set up `redirects` for pages that you've moved or deleted. ([Learn how.](#redirects)) * Set up `rewrites`. ([Learn how.](#rewrites)) * Tweak `gzip` compression for best performance. ([Learn how.](#gzip)) * Customize the `encoding` behavior of your files. ([Learn how.](#encoding-types)) * Allow your project to be embedded as an `iframe`. ([Learn how.](#iframe)) * Customize `assertions` to modify the default verification behavior of the CLI. ([Learn how.](#assertions)) --- ## Where do you define your Hosting configuration? Your hosting configuration is defined in the Juno [configuration](/docs/reference/configuration.md) file, which is automatically created when you run [juno init](/docs/reference/cli.md#init) or [juno deploy](/docs/reference/cli.md#deploy) for the first time. --- ## How do you apply your changes? To apply any changes, execute the [juno config](/docs/reference/cli.md#config) command with the CLI. --- ## Options The list below outlines the available hosting options you can configure to tailor your hosting. ### Source The `source` field specifies the directory that contains the built assets for your satellite. This is typically the output directory generated by your build process after running a command like `npm run build`. Commonly, or if you are using the templates, these are the folders that can be set as the `source` field: | Framework | Source | | --- | --- | | Next.js | `out` | | React, Astro, or Vue | `dist` | | SvelteKit | `build` | | Angular | `dist//browser` | Juno uses this directory to locate the files that will be deployed as part of your satellite. Ensure that this directory includes all the necessary assets, such as HTML, JavaScript, CSS, and any other static or dynamic resources your application requires. juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist" }}); ``` ### Ignore files The `ignore` attribute allows you to exclude certain files from being deployed to your satellite. This attribute works similarly to Git's `.gitignore`, and you can specify which files to ignore using globs. Here is an example of how the ignore attribute can be utilized: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", ignore: ["**/*.txt", ".tmp/"] }}); ``` ### HTTP Headers Headers allow the client and the satellite to pass additional information along with a request or a response. Some sets of headers can affect how the browser handles the page and its content. For instance, you may want to set a specific `Cache-Control` for performance reasons. Here's an example of the `headers` object: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", storage: { headers: [ { source: "/", headers: [["Cache-Control", "public,max-age=0,must-revalidate"]] }, { source: "assets/fonts/*", headers: [["Cache-Control", "max-age=31536000"]] }, { source: "**/*.jpg", headers: [ ["Cache-Control", "max-age=31536000"], ["Access-Control-Allow-Origin", "*"] ] } ] } }}); ``` This `source` attribute works similarly to Git's `.gitignore`, and you can specify which files match the headers using globs. The `headers` is an array of objects, each containing `key` and `value`, and these apply to the matching paths. **Note:** * The `Content-Type` header is calculated automatically and cannot be altered. * No validation or check for uniqueness is performed. For example, if a header matches a file based on multiple rules, multiple headers will be set. * Likewise, if you provide the same header when you [upload](https://juno.build/docs/build/storage#upload-asset) file to your "Storage" and within the configuration, both headers will be set in the response. ### Customize a 404/Not Found page By default, all unknown paths are automatically rewritten to `/index.html`. However, if you wish to serve a custom `404 Not Found` error when a user attempts to access a non-existent page, you can do so without requiring additional configuration. Simply upload a custom `404.html` file to your satellite that should be served from the root path of your site. ### Redirects Use a URL redirect to prevent broken links if you've moved a page or to shorten URLs. For example, you could redirect a browser from `juno.build/start-building` to `juno.build/get-started.html`. Here's the basic structure for a `redirects` attribute. juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", storage: { redirects: [ { source: "/hello", location: "/world/index.html", code: 301 } ] } }}); ``` The `redirects` attribute contains an array of redirect rules: | Field | Description | | --- | --- | | **source** | This `source` attribute works similarly to Git's `.gitignore`, and you can specify which files match the redirects using globs. | | **location** | A relative path to where the browser should make a new request. | | **code** | The HTTPS response code. Use a type of `301` for 'Moved Permanently' or `302` for 'Found' (Temporary Redirect). | ### Rewrites You can utilize optional rewrites to display the same content for multiple URLs. Rewrites are especially useful when combined with pattern matching, allowing acceptance of any URL that matches the pattern. Here's the basic structure for a `rewrites` attribute. juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", storage: { rewrites: [ { source: "/hello/**", destination: "/hello/world.html" } ] } }}); ``` This `source` attribute works similarly to Git's `.gitignore`, and you can specify which files match the rewrites using globs. **Note:** * Rewrites are only applied to requests that do not match any existing resources. * By default, all unknown paths are automatically rewritten to `/index.html` (or `/404.html` if you provide such a page). You cannot disable this default behavior. ### GZIP When deploying your application, the CLI automatically searches for JavaScript (js), ES Module (mjs), and CSS (css) files in the `source` folder and optimizes them using Gzip compression. This is useful because neither the protocol nor a satellite can compress these files, ensuring the best web performance. If you wish to customize this behavior, you have the option to disable it or provide a different file matching pattern using glob syntax. To opt-out of Gzip compression, simply set the `gzip` option to `false` in your configuration: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", gzip: false }}); ``` If you want to customize the default pattern `**/*.+(css|js|mjs)` to better suit your needs, you can specify your own pattern. For example: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", gzip: "**/*.jpg" }}); ``` ### Encoding types When deploying, the CLI automatically maps the encoding type based on the file extension. The encoding information is then used in the satellite to provide the appropriate HTTP response header `Content-Encoding`. The default mappings are as follows: * `.Z` = `compress` * `.gz` = `gzip` * `.br` = `br` * `.zlib` = `deflate` * rest = `identity` (no compression) You can also customize the encoding behavior by using the "encoding" attribute in the configuration file. This attribute works similarly to Git's `.gitignore`, and you can specify which files to ignore using globs. Here is an example of how the "encoding" attribute can be utilized: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", encoding: [["**/releases/*.gz", "identity"]] }}); ``` ### iframe For security reasons and to prevent click-jacking attacks, dapps deployed with Juno are, by default, set to deny embedding in other sites. You can customize this behavior by setting the `iframe` option to either `same-origin`, which restricts your pages to be displayed only if all ancestor frames have the same origin as the page itself, or `allow-any`, which allows your project to be embeddable by any site. juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", storage: { iframe: "same-origin" } }}); ``` ### Assertions The CLI conducts several assertions when interacting with your Satellite, one of which involves monitoring the heap memory size. Typically, the CLI checks to ensure that the heap memory does not exceed the 1 GB limit before deployment. For instance, if your heap memory usage is close to 900 MB, the CLI will prompt you to confirm the deployment. You can customize this behavior by adjusting the heap memory limit in bytes. For example, to set a new limit of 678 MB, update your configuration as follows: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", assertions: { heapMemory: 678000000 } }}); ``` Alternatively, these checks can be completely disabled. To do so, set the `heapMemory` assertion to `false`: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", assertions: { heapMemory: false } }}); ``` # Development This section covers how to configure and manage your custom domain in Juno, including setting up DNS records, troubleshooting common issues, and ensuring compatibility with various domain providers. --- ## Connecting Your Domain To connect your custom domain, follow these steps: 1. Start the custom domain wizard from the Juno console [hosting](https://console.juno.build/hosting) page 2. Enter the desired domain name for your satellite 3. Log into your domain provider's site and configure the DNS records as indicated by Juno 4. Continue the process in Juno Please note that it may take several minutes to set up your custom domain after the wizard is completed and up to 24 hours for your domain provider to reflect the changes. --- ## DNS records To configure DNS records, you will be requested to use `CNAME` records. Some domain providers do not provide such types. Instead, DNS providers often support so-called `CNAME` flattening. To this end, these DNS providers offer flattened record types, such as `ANAME` or `ALIAS` records, which can be used instead of the `CNAME` to `icp1.io`. Some DNS providers require you to specify the main domain. For example, you might have to specify your full domain `foo.bar.com` for the `CNAME` entry related to `icp1.io` instead of only `foo` as displayed by our [console](/docs/terminology.md#console). If you ever encounter issues configuring your DNS, you can also refer to the [Troubleshooting](https://internetcomputer.org/docs/current/developer-docs/production/custom-domain/#troubleshooting) section for further assistance. ### Cloudflare The DNS entries presented in the console are exactly the ones that should be configured in Cloudflare. However, based on our experience, to enable the custom domain properly, the following settings in Cloudflare should be disabled: * DNS > Settings > Disable DNSSEC * SSL/TLS > Overview > Set "Your SSL/TLS encryption mode" to "Off (not secure)". A SSL certificate will be automatically ordered by configuring the custom domain. * SSL/TLS > Edge Certificates > Disable Universal SSL Additionally, we recommend not proxying the DNS entries ("DNS only"). ### Namecheap This external guide provides instructions on how to configure the DNS records for [Namecheap](https://internetcomputer.org/docs/current/developer-docs/production/custom-domain/dns-setup#namecheap). ### GoDaddy This external guide provides instructions on how to configure the DNS records for [GoDaddy](https://internetcomputer.org/docs/current/developer-docs/production/custom-domain/dns-setup#godaddy). ### Google Domains Google Domains does not support `CNAME` records for the apex domain. For this reason, we suggest transferring the domain to ([Cloudflare](#cloudflare)). ### Other Limited Providers We are aware of a few other providers that also do not support `CNAME` records for the apex domain. Similar to Google Domains, we advise transferring the domain to ([Cloudflare](#cloudflare)) in order to overcome this limitation. * HostGator * Infomaniak --- ## Status The status of the configuration of your custom domain can be one of the following: **Tip:** ⏳ DNS propagation can take time! It may take several minutes to hours for changes to fully take effect. If your domain is not working as expected, avoid making multiple changes, as this can cause further delays. If you're having trouble, feel free to reach out for assistance—we’re happy to help! * `PendingOrder`: The registration request has been submitted and is waiting to be picked up. * `PendingChallengeResponse`: The certificate has been ordered. * `PendingAcmeApproval`: The challenge has been completed. * `Available`: The registration request has been successfully processed. Your custom domain is ready. * `Failed`: The registration request failed. If one of the status `Pending...` is reached, the console will automatically refresh the status every few seconds until your domain is available. # Collections You can create or update a collection in the "Collections" tab in Juno's console under the [storage](https://console.juno.build/storage) view. --- ## Configuration Each collection has a set of configurable options that define its behavior and limitations: | Option | Mandatory | Description | | --- | --- | --- | | Key | Yes | A unique identifier for the collection. The key that you will use in your code to interact with a particular collection. | | Read permission | Yes | Defines who can read documents in the collection. See ([Permissions](#permissions)) below. | | Write permission | Yes | Defines who can create, update, or delete documents. See ([Permissions](#permissions)) below. | | Memory | Yes | Specifies whether the collection uses `heap` or `stable` memory. This setting is permanent and cannot be changed after creation. The default is `stable` memory. For more information, see the related [documentation](/docs/miscellaneous/memory.md). | | Max changes per user | No | Limits the number of documents a single user can create, update, or delete in the collection. This helps maintain fair resource distribution across users. | | Max size | No | Sets an optional limit (in bytes) on the maximum size of individual assets that can be uploaded to a collection. | | Max updates per minute | No | Limits the number of creation, update and delete operations per minute to prevent excessive updates. | | Immutable permissions | No | If enabled, read and write permissions cannot be modified after creation. | --- ## Permissions Permissions define who can read and write assets in a collection. Writing includes creating (uploading), updating, and deleting assets. **Caution:** Assets are publicly accessible on the Internet regardless of the permission schema. The rules are only applied when reading or writing the data through the library. | Permission | Description | | --- | --- | | **Public** | Anyone can read or write assets in the collection. | | **Private** | Only the creator (owner) of the asset can read or write to it. However, note that since satellite administrators manage the underlying infrastructure, they have the technical ability to modify access rules by changing its source code. | | **Managed** | The owner of an asset, the administrator and editor of the satellite can read or write to it in the collection. | | **Restricted** | Only satellite administrator and editor can read or write any asset in the collection. | If not set to immutable, you can modify the permissions at any time, and the changes will take effect immediately. **Tip:** Any collection with read permissions set to `public`, `managed` or `restricted` will allow the developer to view its content in the console under the [storage](https://console.juno.build/storage) view. # Development This page explains how to manage assets using the Juno SDK, including uploading, listing, counting, and deleting files within your application. It also covers configuration options for optimizing storage and access control. **Important:** The functions described on this page are intended for use in the **client-side** of your app. If you're looking to extend backend capabilities using serverless logic, refer to the [Functions documentation](/docs/build/functions.md). --- ## Upload asset To upload an asset, use the following code: ``` import { uploadFile } from "@junobuild/core";const result = await uploadFile({ data, collection: "images"}); ``` The `data` parameter is the file you want to upload. This is typically selected using an HTML `` element. The `uploadFile` function provides various options, including: * `filename`: By default, Juno uses the file's filename. You can overwrite this and provide a custom filename. Example: `myimage.jpg`. * `fullPath`: Juno will automatically compute the `fullPath`, which is the **unique** path that is used to make the asset available on the internet. The `fullPath` is the filename prefixed with `/` plus the related collection key. Example: `/images/myimage.jpg`. * `headers`: The headers can affect how the browser handles the asset. If no headers are provided Juno will infer the `Content-Type` from the file type. * `encoding`: The type of encoding for the file. For example, `identity` (raw) or `gzip`. **Note:** * Uploading a file with the same name as an existing file will overwrite the previous file (assuming the uploader has write access to the previous file). * URL encoding is currently not supported on the Internet Computer. Therefore, it's important to keep in mind that your `filename` should not be encoded. That is why the library decodes the `filename` automatically. ### Protected asset While all assets can be found on the internet, it is possible to make their URL difficult to guess so that they remain undiscoverable (**as long as they are not shared**) and considered "private". Juno achieves this by using an optional `token` query parameter. ``` import { uploadFile } from "@junobuild/core";import { nanoid } from "nanoid";const result = await uploadFile({ data, collection: "images", token: nanoid()}); ``` Imagine a file "mydata.jpg" uploaded with a token. Attempting to access it through the URL "[https://yoursatellite/mydata.jpg](https://yoursatellite/mydata.jpg)" will not work. The asset can only be retrieved if a token is provided: "[https://yoursatellite/mydata.jpg?token=a-super-long-secret-id](https://yoursatellite/mydata.jpg?token=a-super-long-secret-id)". --- ## List assets The `listAssets` function is used to retrieve assets from a specified collection. ``` import { listAssets } from "@junobuild/core";const myList = await listAssets({ collection: "my_collection_key"}); ``` ### Parameters The function requires a collection and accepts various optional parameters, including a matcher (a regex applied to the assets fullPaths and descriptions), pagination options, and sorting order. **Note:** `listAssets` uses the same interface as `listDocs`. That is why the parameter `matcher` expect a value `key` to filter the assets according their `fullPath`. 1. **`collection`** (required) * **Description**: The key of the collection from which assets are to be listed. * **Type**: `string` 2. **`filter`** (optional) * **Description**: An optional object that can be used to provide various parameters to filter assets. a. **`matcher`** (optional) * **Description**: An object used to filter assets based on their keys (fullPaths) or descriptions using regular expressions. * **Type**: `ListMatcher` ``` interface ListMatcher { key?: string; description?: string; createdAt?: ListTimestampMatcher; updatedAt?: ListTimestampMatcher;} ``` * **key**: A regex to match against asset keys. * **description**: A regex to match against asset descriptions. * **createdAt**: A `ListTimestampMatcher` to filter assets based on their creation timestamp. * **updatedAt**: A `ListTimestampMatcher` to filter assets based on their last update timestamp. * **Type**: `ListTimestampMatcher` can be used to specify criteria for timestamp matching. ``` type ListTimestampMatcher = | { matcher: "equal"; timestamp: bigint; } | { matcher: "greaterThan"; timestamp: bigint; } | { matcher: "lessThan"; timestamp: bigint; } | { matcher: "between"; timestamps: { start: bigint; end: bigint; }; }; ``` * **matcher**: Specifies the type of timestamp comparison. Can be one of the following: * **equal**: Matches assets where the timestamp is exactly equal to the specified value. * **greaterThan**: Matches assets where the timestamp is greater than the specified value. * **lessThan**: Matches assets where the timestamp is less than the specified value. * **between**: Matches assets where the timestamp falls within a specified range. * **timestamp**: Used with `equal`, `greaterThan`, and `lessThan` matchers to specify the exact timestamp for comparison. * **timestamps**: Used with the `between` matcher to specify a range of timestamps. The range is inclusive of both the start and end values. b. **`paginate`** (optional) * **Description**: An object to control pagination of the results * **Type**: `ListPaginate` ``` interface ListPaginate { startAfter?: string; limit?: number;} ``` * **startAfter**: A string key to start listing assets after this key. * **limit**: The maximum number of assets to return. c. **`order`** (optional) * **Description**: Control the sorting order of the results. * **Type**: `ListOrder` ``` interface ListOrder { desc: boolean; field: ListOrderField;}type ListOrderField = "keys" | "updated_at" | "created_at"; ``` d. **`owner`** (optional) * **Description**: The owner of the assets. * **Type**: `ListOwner` ``` type ListOwner = string | Principal; ``` **Example:** Usage of the parameters: ``` import { listDocs } from "@junobuild/core";const myList = await listDocs({ collection: "my_collection_key", filter: { matcher: { key: ".*.png$", // match assets with .png extension description: "holiday", // match description containing 'holiday' createdAt: { matcher: "greaterThan", timestamp: 1627776000n }, updatedAt: { matcher: "between", timestamps: { start: 1627770000n, end: 1627900000n } } }, paginate: { startAfter: "doc_10", limit: 5 }, order: { desc: true, field: "updated_at" }, owner: "some_owner_id_or_principal" }}); ``` The function returns the assets and various information, in the form of an object whose interface is given below. ``` { items: []; // The data - array of assets items_length: bigint; // The number of assets - basically items.length items_page?: bigint; // If the query is paginated, at what page (starting from 0) do the items find the place matches_length: bigint; // The total number of matching results matches_pages?: bigint; // If the query is paginated, the total number (starting from 0) of pages} ``` --- ## Count assets The `countAssets` function is used to count the number of assets in a specified collection without retrieving the actual assets. ``` import { countAssets } from "@junobuild/core";const assetCount = await countAssets({ collection: "my_collection_key"}); ``` ### Usage This function accepts similar parameters as the `listAssets` function, including `collection`, `matcher`, and `owner`, and returns the count of matching documents. For detailed information on how to use these parameters, refer to the ([List assets](#list-assets)) section. The return value is the same as the `items_length` property from the `listAssets` function, providing the count of assets that match the criteria. --- ## Delete There are multiple ways to delete assets from your Storage. ### Delete asset To delete an asset, you only need to provide its `fullPath`. Unlike the [datastore](/docs/build/datastore.md), there is no timestamp validation performed when deleting an asset. ``` import { deleteAsset } from "@junobuild/core";await deleteAsset({ collection: "images", storageFile: myAsset}); ``` ### Delete multiple assets To delete multiple assets in an atomic manner, you can use the function `deleteManyAssets`: ``` import { deleteManyAssets } from "@junobuild/core";const myAsset1 = { collection: "hello", fullPath: "/hello/world.jpg"};const myAsset2 = { collection: "data", fullPath: "/data/something.json"};await deleteManyAssets({ assets: [myAsset1, myAsset2] }); ``` ### Delete filtered assets The `deleteFilteredAssets` function allows you to delete multiple assets from a collection based on specific filter criteria. This function simplifies bulk deletions by leveraging the same parameters as the ([listAssets](#list-assets)) function for filtering. ``` import { deleteFilteredAssets } from "@junobuild/core";await deleteFilteredAssets({ collection: "my_collection_key", filter: { // Same options as filter of listAssets }}); ``` # Angular Example This project is a note-taking app template built with **Angular**, **TypeScript**, and **Tailwind CSS**, designed to demonstrate integration with Juno for app development. It showcases authentication, data storage, and file storage using Juno's Satellite container. You can scaffold it using the following command, or browse the source code: ``` npm create juno@latest -- --template react-example ``` Source: [github.com/junobuild/create-juno/templates/angular-example](https://github.com/junobuild/create-juno/tree/main/templates/angular-example) --- ## Folder Structure ``` angular-example/├── public/ # Static assets├── src/│ ├── app/ # Angular modules, components, services, and types│ ├── environments/ # Environment configuration files│ ├── styles.css # Tailwind CSS styles│ └── main.ts # Angular entry point├── juno.config.mjs # Juno Satellite configuration├── package.json # Project dependencies and scripts├── angular.json # Angular CLI configuration├── README.md # User-facing documentation└── ... # Other config and build files ``` --- ## Key Features * **Juno Integration**: Uses Juno's Satellite for authentication, Datastore, and Storage. * **Authentication**: Login/logout flow. * **Notes Collection**: Users can create, view, and delete notes (text, with optional file URL). * **Images Collection**: Supports file storage for images. * **Responsive UI**: Built with Tailwind CSS for modern styling. * **Banner**: Warns if the Satellite is not configured for local development. --- ## Main Components * **app.component.ts**: Main Angular component, bootstraps the app and layout. * **components/**: Contains UI and logic for authentication, notes table, modal, banner, etc. * **services/**: Angular services for interacting with Juno and managing app state. * **types/note.ts**: TypeScript interface for notes. --- ## Data Structure * **Note** (`src/app/types/note.ts`): ``` export interface Note { text: string; url?: string;} ``` --- ## How to Run 1. **Install dependencies**: ``` npm install ``` 2. **Start Juno local emulator**: **Important:** Requires the Juno CLI to be available `npm i -g @junobuild/cli` ``` juno dev start ``` 3. **Create a Satellite** for local dev: * Visit [http://localhost:5866](http://localhost:5866) and follow the instructions. * Update `src/environments/environment.ts` with your Satellite ID. 4. **Create required collections**: * `notes` in Datastore: [http://localhost:5866/datastore](http://localhost:5866/datastore) * `images` in Storage: [http://localhost:5866/storage](http://localhost:5866/storage) 5. **Start the frontend dev server** (in a separate terminal): ``` npm run start ``` --- ## Juno-Specific Configuration * **juno.config.mjs**: Defines Satellite IDs for development/production, build source, and predeploy steps. * **src/environments/environment.ts**: Contains the Satellite ID for local development. * **src/environments/environment.prod.ts**: Contains the Satellite ID for production. --- ## Production Deployment * Create a Satellite on the [Juno Console](https://console.juno.build) for mainnet. * Update `src/environments/environment.prod.ts` and `juno.config.mjs` with the production Satellite ID. * Build and deploy: ``` npm run buildjuno deploy ``` --- ## Notes * The app is intended as a starting point for Juno-based projects. * All logic is in TypeScript and Angular components/services. * The app is fully client-side (Server Side Rendering is not supported yet) and interacts with Juno via the Satellite container. --- ## Juno SDK Used The following functions from `@junobuild/core` are used in this example: | Function | Purpose/Description | Where Used (File/Component) | Juno Docs/Source Reference | | --- | --- | --- | --- | | `initSatellite` | Initialize Juno Satellite container | [`src/app/app.component.ts`](https://github.com/junobuild/create-juno/blob/main/templates/angular-example/src/app/app.component.ts) | [Initialization](/docs/setup-the-sdk.md#initialization) | | `authSubscribe` | Subscribe to auth state changes | [`src/app/services/auth.service.ts`](https://github.com/junobuild/create-juno/blob/main/templates/angular-example/src/app/services/auth.service.ts) | [Subscription](/docs/build/authentication/development.md#subscription) | | `signIn` | Sign in user | [`src/app/components/login/login.component.ts`](https://github.com/junobuild/create-juno/blob/main/templates/angular-example/src/app/components/login/login.component.ts) | [Sign-in](/docs/build/authentication/development.md#sign-in) | | `signOut` | Sign out user | [`src/app/components/logout/logout.component.ts`](https://github.com/junobuild/create-juno/blob/main/templates/angular-example/src/app/components/logout/logout.component.ts) | [Sign-out](/docs/build/authentication/development.md#sign-out) | | `listDocs` | List documents in a collection | [`src/app/services/docs.service.ts`](https://github.com/junobuild/create-juno/blob/main/templates/angular-example/src/app/services/docs.service.ts) | [List documents](/docs/build/datastore/development.md#list-documents) | | `setDoc` | Create or update a document | [`src/app/components/modal/modal.component.ts`](https://github.com/junobuild/create-juno/blob/main/templates/angular-example/src/app/components/modal/modal.component.ts) | [Add a document](/docs/build/datastore/development.md#add-a-document) | | `deleteDoc` | Delete a document | [`src/app/components/delete/delete.component.ts`](https://github.com/junobuild/create-juno/blob/main/templates/angular-example/src/app/components/delete/delete.component.ts) | [Delete a document](/docs/build/datastore/development.md#delete-a-document) | | `uploadFile` | Upload a file to storage | [`src/app/components/modal/modal.component.ts`](https://github.com/junobuild/create-juno/blob/main/templates/angular-example/src/app/components/modal/modal.component.ts) | [Upload asset](/docs/build/storage/development.md#upload-asset) | | `deleteAsset` | Delete a file from storage | [`src/app/components/delete/delete.component.ts`](https://github.com/junobuild/create-juno/blob/main/templates/angular-example/src/app/components/delete/delete.component.ts) | [Delete asset](/docs/build/storage/development.md#delete-asset) | # Next.js Example This project is a note-taking app template built with **Next.js**, **TypeScript**, and **Tailwind CSS**, designed to demonstrate integration with Juno for app development. It showcases authentication, data storage, and file storage using Juno's Satellite container. You can scaffold it using the following command, or browse the source code: ``` npm create juno@latest -- --template nextjs-example ``` Source: [github.com/junobuild/create-juno/templates/nextjs-example](https://github.com/junobuild/create-juno/tree/main/templates/nextjs-example) --- ## Folder Structure ``` nextjs-example/├── public/ # Static assets├── src/│ ├── app/ # Next.js app directory (routing, layout, etc.)│ ├── components/ # React UI components (auth, table, modal, banner, etc.)│ ├── types/ # TypeScript types (e.g., note.ts)├── juno.config.mjs # Juno Satellite configuration├── package.json # Project dependencies and scripts├── next.config.mjs # Next.js configuration├── README.md # User-facing documentation└── ... # Other config and build files ``` --- ## Key Features * **Juno Integration**: Uses Juno's Satellite for authentication, Datastore, and Storage. * **Authentication**: Login/logout flow. * **Notes Collection**: Users can create, view, and delete notes (text, with optional file URL). * **Images Collection**: Supports file storage for images. * **Responsive UI**: Built with Tailwind CSS for modern styling. * **Banner**: Warns if the Satellite is not configured for local development. --- ## Main Components * **src/app/**: Next.js app directory, handles routing and layout. * **components/**: Contains UI and logic for authentication, notes table, modal, banner, etc. * **types/note.ts**: TypeScript interface for notes. --- ## Data Structure * **NoteData** (`src/types/note.ts`): ``` export interface NoteData { text: string; url?: string;}export type Note = Doc; ``` --- ## How to Run 1. **Install dependencies**: ``` npm install ``` 2. **Start Juno local emulator**: **Important:** Requires the Juno CLI to be available `npm i -g @junobuild/cli` ``` juno dev start ``` 3. **Create a Satellite** for local dev: * Visit [http://localhost:5866](http://localhost:5866) and follow the instructions. * Update `juno.config.mjs` with your Satellite ID. 4. **Create required collections**: * `notes` in Datastore: [http://localhost:5866/datastore](http://localhost:5866/datastore) * `images` in Storage: [http://localhost:5866/storage](http://localhost:5866/storage) 5. **Start the frontend dev server** (in a separate terminal): ``` npm run start ``` --- ## Juno-Specific Configuration * **juno.config.mjs**: Defines Satellite IDs for development/production, build source, and predeploy steps. --- ## Production Deployment * Create a Satellite on the [Juno Console](https://console.juno.build) for mainnet. * Update `juno.config.mjs` with the production Satellite ID. * Build and deploy: ``` npm run buildjuno deploy ``` --- ## Notes * The app is intended as a starting point for Juno-based projects. * All logic is in TypeScript and React function components. * The app is fully client-side (Server Side Rendering is not supported yet) and interacts with Juno via the Satellite container. --- ## Juno SDK Used The following functions from `@junobuild/core` are used in this example: | Function | Purpose/Description | Where Used (File/Component) | Juno Docs/Source Reference | | --- | --- | --- | --- | | `initSatellite` | Initialize Juno Satellite container | [`src/app/page.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/nextjs-example/src/app/page.tsx) | [Initialization](/docs/setup-the-sdk.md#initialization) | | `authSubscribe` | Subscribe to auth state changes | [`src/components/auth.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/nextjs-example/src/components/auth.tsx) | [Subscription](/docs/build/authentication/development.md#subscription) | | `signIn` | Sign in user | [`src/components/login.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/nextjs-example/src/components/login.tsx) | [Sign-in](/docs/build/authentication/development.md#sign-in) | | `signOut` | Sign out user | [`src/components/logout.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/nextjs-example/src/components/logout.tsx) | [Sign-out](/docs/build/authentication/development.md#sign-out) | | `listDocs` | List documents in a collection | [`src/components/table.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/nextjs-example/src/components/table.tsx) | [List documents](/docs/build/datastore/development.md#list-documents) | | `setDoc` | Create or update a document | [`src/components/modal.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/nextjs-example/src/components/modal.tsx) | [Add a document](/docs/build/datastore/development.md#add-a-document) | | `deleteDoc` | Delete a document | [`src/components/delete.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/nextjs-example/src/components/delete.tsx) | [Delete a document](/docs/build/datastore/development.md#delete-a-document) | | `uploadFile` | Upload a file to storage | [`src/components/modal.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/nextjs-example/src/components/modal.tsx) | [Upload asset](/docs/build/storage/development.md#upload-asset) | | `deleteAsset` | Delete a file from storage | [`src/components/delete.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/nextjs-example/src/components/delete.tsx) | [Delete asset](/docs/build/storage/development.md#delete-asset) | # React JavaScript Example This project is a note-taking app template built with **React**, **JavaScript**, and **Tailwind CSS**, designed to demonstrate integration with Juno for app development. It showcases authentication, data storage, and file storage using Juno's Satellite container. You can scaffold it using the following command, or browse the source code: ``` npm create juno@latest -- --template react-example ``` Source: [github.com/junobuild/create-juno/templates/react-example](https://github.com/junobuild/create-juno/tree/main/templates/react-example) --- ## Folder Structure ``` react-example/├── public/ # Static assets├── src/│ ├── components/ # React UI components (Auth, Table, Modal, Banner, etc.)│ ├── App.jsx # Main app component│ ├── main.jsx # React entry point│ └── index.css # Tailwind CSS styles├── juno.config.mjs # Juno Satellite configuration├── package.json # Project dependencies and scripts├── vite.config.js # Vite build configuration├── README.md # User-facing documentation└── ... # Other config and build files ``` --- ## Key Features * **Juno Integration**: Uses Juno's Satellite for authentication, Datastore, and Storage. * **Authentication**: Login/logout flow. * **Notes Collection**: Users can create, view, and delete notes (text, with optional file URL). * **Images Collection**: Supports file storage for images. * **Responsive UI**: Built with Tailwind CSS for modern styling. * **Banner**: Warns if the Satellite is not configured for local development. --- ## Main Components * **App.jsx**: Initializes Juno Satellite, renders the main layout, and wraps content in authentication. * **Banner.jsx**: Shows a warning if the Satellite ID is missing in local dev. * **Auth.jsx**: Provides authentication context and login/logout UI. * **Table.jsx**: Displays the list of notes from the Datastore. * **Modal.jsx**: Handles note creation and editing. * **Delete.jsx**: Handles note deletion. * **Footer.jsx, Background.jsx, Button.jsx, etc.**: UI and utility components. --- ## Data Structure * **Note** (used in Table.jsx): ``` // Example note object{ key: string, data: { text: string, url?: string }} ``` --- ## How to Run 1. **Install dependencies**: ``` npm install ``` 2. **Start Juno local emulator**: **Important:** Requires the Juno CLI to be available `npm i -g @junobuild/cli` ``` juno dev start ``` 3. **Create a Satellite** for local dev: * Visit [http://localhost:5866](http://localhost:5866) and follow the instructions. * Update `juno.config.mjs` with your Satellite ID. 4. **Create required collections**: * `notes` in Datastore: [http://localhost:5866/datastore](http://localhost:5866/datastore) * `images` in Storage: [http://localhost:5866/storage](http://localhost:5866/storage) 5. **Start the frontend dev server** (in a separate terminal): ``` npm run start ``` --- ## Juno-Specific Configuration * **juno.config.mjs**: Defines Satellite IDs for development/production, build source, and predeploy steps. --- ## Production Deployment * Create a Satellite on the [Juno Console](https://console.juno.build) for mainnet. * Update `juno.config.mjs` with the production Satellite ID. * Build and deploy: ``` npm run buildjuno deploy ``` --- ## Notes * The app is intended as a starting point for Juno-based projects. * All logic is in JavaScript and React function components. * The app is fully client-side (Server Side Rendering is not supported yet) and interacts with Juno via the Satellite container. --- ## Juno SDK Used The following functions from `@junobuild/core` are used in this example: | Function | Purpose/Description | Where Used (File/Component) | Juno Docs/Source Reference | | --- | --- | --- | --- | | `initSatellite` | Initialize Juno Satellite container | [`src/App.jsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-example/src/App.jsx) | [Initialization](/docs/setup-the-sdk.md#initialization) | | `authSubscribe` | Subscribe to auth state changes | [`src/components/Auth.jsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-example/src/components/Auth.jsx) | [Subscription](/docs/build/authentication/development.md#subscription) | | `signIn` | Sign in user | [`src/components/Login.jsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-example/src/components/Login.jsx) | [Sign-in](/docs/build/authentication/development.md#sign-in) | | `signOut` | Sign out user | [`src/components/Logout.jsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-example/src/components/Logout.jsx) | [Sign-out](/docs/build/authentication/development.md#sign-out) | | `listDocs` | List documents in a collection | [`src/components/Table.jsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-example/src/components/Table.jsx) | [List documents](/docs/build/datastore/development.md#list-documents) | | `setDoc` | Create or update a document | [`src/components/Modal.jsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-example/src/components/Modal.jsx) | [Add a document](/docs/build/datastore/development.md#add-a-document) | | `deleteDoc` | Delete a document | [`src/components/Delete.jsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-example/src/components/Delete.jsx) | [Delete a document](/docs/build/datastore/development.md#delete-a-document) | | `uploadFile` | Upload a file to storage | [`src/components/Modal.jsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-example/src/components/Modal.jsx) | [Upload asset](/docs/build/storage/development.md#upload-asset) | | `deleteAsset` | Delete a file from storage | [`src/components/Delete.jsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-example/src/components/Delete.jsx) | [Delete asset](/docs/build/storage/development.md#delete-asset) | # React TypeScript Example This project is a note-taking app template built with **React**, **TypeScript**, and **Tailwind CSS**, designed to demonstrate integration with Juno for app development. It showcases authentication, data storage, and file storage using Juno's Satellite container. You can scaffold it using the following command, or browse the source code: ``` npm create juno@latest -- --template react-ts-example ``` Source: [github.com/junobuild/create-juno/templates/react-ts-example](https://github.com/junobuild/create-juno/tree/main/templates/react-ts-example) --- ## Folder Structure ``` react-ts-example/├── public/ # Static assets├── src/│ ├── components/ # React UI components (Auth, Table, Modal, Banner, etc.)│ ├── types/ # TypeScript types (e.g., note.ts)│ ├── App.tsx # Main app component│ ├── main.tsx # React entry point│ └── index.css # Tailwind CSS styles├── juno.config.ts # Juno Satellite configuration├── package.json # Project dependencies and scripts├── vite.config.ts # Vite build configuration├── README.md # User-facing documentation└── ... # Other config and build files ``` --- ## Key Features * **Juno Integration**: Uses Juno's Satellite for authentication, Datastore, and Storage. * **Authentication**: Login/logout flow. * **Notes Collection**: Users can create, view, and delete notes (text, with optional file URL). * **Images Collection**: Supports file storage for images. * **Responsive UI**: Built with Tailwind CSS for modern styling. * **Banner**: Warns if the Satellite is not configured for local development. --- ## Main Components * **App.tsx**: Initializes Juno Satellite, renders the main layout, and wraps content in authentication. * **Banner.tsx**: Shows a warning if the Satellite ID is missing in local dev. * **Auth.tsx**: Provides authentication context and login/logout UI. * **Table.tsx**: Displays the list of notes from the Datastore. * **Modal.tsx**: Handles note creation and editing. * **Delete.tsx**: Handles note deletion. * **Footer.tsx, Background.tsx, Button.tsx, etc.**: UI and utility components. --- ## Data Types * **NoteData** (`src/types/note.ts`): ``` export interface NoteData { text: string; url?: string;}export type Note = Doc; ``` --- ## How to Run 1. **Install dependencies**: ``` npm install ``` 2. **Start Juno local emulator**: **Important:** Requires the Juno CLI to be available `npm i -g @junobuild/cli` ``` juno dev start ``` 3. **Create a Satellite** for local dev: * Visit [http://localhost:5866](http://localhost:5866) and follow the instructions. * Update `juno.config.ts` with your Satellite ID. 4. **Create required collections**: * `notes` in Datastore: [http://localhost:5866/datastore](http://localhost:5866/datastore) * `images` in Storage: [http://localhost:5866/storage](http://localhost:5866/storage) 5. **Start the frontend dev server** (in a separate terminal): ``` npm run start ``` --- ## Juno-Specific Configuration * **juno.config.ts**: Defines Satellite IDs for development/production, build source, and predeploy steps. --- ## Production Deployment * Create a Satellite on the [Juno Console](https://console.juno.build) for mainnet. * Update `juno.config.ts` with the production Satellite ID. * Build and deploy: ``` npm run buildjuno deploy ``` ## Notes * The app is intended as a starting point for Juno-based projects. * All logic is in JavaScript and React function components. * The app is fully client-side (Server Side Rendering is not supported yet) and interacts with Juno via the Satellite container. --- ## Juno SDK Used The following functions from `@junobuild/core` are used in this example: | Function | Purpose/Description | Where Used (File/Component) | Juno Docs/Source Reference | | --- | --- | --- | --- | | `initSatellite` | Initialize Juno Satellite container | [`src/App.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-ts-example/src/App.tsx) | [Initialization](/docs/setup-the-sdk.md#initialization) | | `authSubscribe` | Subscribe to auth state changes | [`src/components/Auth.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-ts-example/src/components/Auth.tsx) | [Subscription](/docs/build/authentication/development.md#subscription) | | `signIn` | Sign in user | [`src/components/Login.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-ts-example/src/components/Login.tsx) | [Sign-in](/docs/build/authentication/development.md#sign-in) | | `signOut` | Sign out user | [`src/components/Logout.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-ts-example/src/components/Logout.tsx) | [Sign-out](/docs/build/authentication/development.md#sign-out) | | `listDocs` | List documents in a collection | [`src/components/Table.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-ts-example/src/components/Table.tsx) | [List documents](/docs/build/datastore/development.md#list-documents) | | `setDoc` | Create or update a document | [`src/components/Modal.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-ts-example/src/components/Modal.tsx) | [Add a document](/docs/build/datastore/development.md#add-a-document) | | `deleteDoc` | Delete a document | [`src/components/Delete.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-ts-example/src/components/Delete.tsx) | [Delete a document](/docs/build/datastore/development.md#delete-a-document) | | `uploadFile` | Upload a file to storage | [`src/components/Modal.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-ts-example/src/components/Modal.tsx) | [Upload asset](/docs/build/storage/development.md#upload-asset) | | `deleteAsset` | Delete a file from storage | [`src/components/Delete.tsx`](https://github.com/junobuild/create-juno/blob/main/templates/react-ts-example/src/components/Delete.tsx) | [Delete asset](/docs/build/storage/development.md#delete-asset) | # SvelteKit Example This project is a note-taking app template built with **SvelteKit**, **TypeScript**, and **Tailwind CSS**, designed to demonstrate integration with Juno for app development. It showcases authentication, data storage, and file storage using Juno's Satellite container. You can scaffold it using the following command, or browse the source code: ``` npm create juno@latest -- --template sveltekit-example ``` Source: [github.com/junobuild/create-juno/templates/sveltekit-example](https://github.com/junobuild/create-juno/tree/main/templates/sveltekit-example) --- ## Folder Structure ``` sveltekit-example/├── static/ # Static assets├── src/│ ├── lib/ # SvelteKit components, stores, types, etc.│ ├── routes/ # SvelteKit routes and layouts│ ├── app.css # Tailwind CSS styles│ └── app.html # SvelteKit HTML template├── juno.config.ts # Juno Satellite configuration├── package.json # Project dependencies and scripts├── vite.config.ts # Vite build configuration├── README.md # User-facing documentation└── ... # Other config and build files ``` --- ## Key Features * **Juno Integration**: Uses Juno's Satellite for authentication, Datastore, and Storage. * **Authentication**: Login/logout flow. * **Notes Collection**: Users can create, view, and delete notes (text, with optional file URL). * **Images Collection**: Supports file storage for images. * **Responsive UI**: Built with Tailwind CSS for modern styling. * **Banner**: Warns if the Satellite is not configured for local development. --- ## Main Components * **src/routes/+layout.svelte**: Main layout, initializes Juno Satellite, wraps content in authentication. * **lib/components/**: Contains UI and logic for authentication, notes table, modal, banner, etc. * **lib/types/note.ts**: TypeScript interface for notes. * **lib/types/user.ts**: TypeScript interface for user. --- ## Data Structure * **Note** (`src/lib/types/note.ts`): ``` export interface Note { text: string; url?: string;} ``` --- ## How to Run 1. **Install dependencies**: ``` npm install ``` 2. **Start Juno local emulator**: **Important:** Requires the Juno CLI to be available `npm i -g @junobuild/cli` ``` juno dev start ``` 3. **Create a Satellite** for local dev: * Visit [http://localhost:5866](http://localhost:5866) and follow the instructions. * Update `juno.config.ts` with your Satellite ID. 4. **Create required collections**: * `notes` in Datastore: [http://localhost:5866/datastore](http://localhost:5866/datastore) * `images` in Storage: [http://localhost:5866/storage](http://localhost:5866/storage) 5. **Start the frontend dev server** (in a separate terminal): ``` npm run start ``` --- ## Juno-Specific Configuration * **juno.config.ts**: Defines Satellite IDs for development/production, build source, and predeploy steps. --- ## Production Deployment * Create a Satellite on the [Juno Console](https://juno.build) for mainnet. * Update `juno.config.ts` with the production Satellite ID. * Build and deploy: ``` npm run buildjuno deploy ``` --- ## Notes * The app is intended as a starting point for Juno-based projects. * All logic is in TypeScript and SvelteKit components/stores. * The app is fully client-side (Server Side Rendering is not supported yet) and interacts with Juno via the Satellite container. --- ## Juno SDK Used The following functions from `@junobuild/core` are used in this example: | Function | Purpose/Description | Where Used (File/Component) | Juno Docs/Source Reference | | --- | --- | --- | --- | | `initSatellite` | Initialize Juno Satellite container | [`src/routes/+layout.svelte`](https://github.com/junobuild/create-juno/blob/main/templates/sveltekit-example/src/routes/+layout.svelte) | [Initialization](/docs/setup-the-sdk.md#initialization) | | `authSubscribe` | Subscribe to auth state changes | [`src/lib/components/Auth.svelte`](https://github.com/junobuild/create-juno/blob/main/templates/sveltekit-example/src/lib/components/Auth.svelte) | [Subscription](/docs/build/authentication/development.md#subscription) | | `signIn` | Sign in user | [`src/lib/components/Login.svelte`](https://github.com/junobuild/create-juno/blob/main/templates/sveltekit-example/src/lib/components/Login.svelte) | [Sign-in](/docs/build/authentication/development.md#sign-in) | | `signOut` | Sign out user | [`src/lib/components/Logout.svelte`](https://github.com/junobuild/create-juno/blob/main/templates/sveltekit-example/src/lib/components/Logout.svelte) | [Sign-out](/docs/build/authentication/development.md#sign-out) | | `listDocs` | List documents in a collection | [`src/lib/components/Table.svelte`](https://github.com/junobuild/create-juno/blob/main/templates/sveltekit-example/src/lib/components/Table.svelte) | [List documents](/docs/build/datastore/development.md#list-documents) | | `setDoc` | Create or update a document | [`src/lib/components/Modal.svelte`](https://github.com/junobuild/create-juno/blob/main/templates/sveltekit-example/src/lib/components/Modal.svelte) | [Add a document](/docs/build/datastore/development.md#add-a-document) | | `deleteDoc` | Delete a document | [`src/lib/components/Delete.svelte`](https://github.com/junobuild/create-juno/blob/main/templates/sveltekit-example/src/lib/components/Delete.svelte) | [Delete a document](/docs/build/datastore/development.md#delete-a-document) | | `uploadFile` | Upload a file to storage | [`src/lib/components/Modal.svelte`](https://github.com/junobuild/create-juno/blob/main/templates/sveltekit-example/src/lib/components/Modal.svelte) | [Upload asset](/docs/build/storage/development.md#upload-asset) | | `deleteAsset` | Delete a file from storage | [`src/lib/components/Delete.svelte`](https://github.com/junobuild/create-juno/blob/main/templates/sveltekit-example/src/lib/components/Delete.svelte) | [Delete asset](/docs/build/storage/development.md#delete-asset) | # Vanilla JavaScript Example This project is a note-taking app template built with **vanilla JavaScript** and **Tailwind CSS**, designed to demonstrate integration with Juno for app development. It showcases authentication, data storage, and file storage using Juno's Satellite container. You can scaffold it using the following command, or browse the source code: ``` npm create juno@latest -- --template vanilla-js-example ``` Source: [github.com/junobuild/create-juno/templates/vanilla-js-example](https://github.com/junobuild/create-juno/tree/main/templates/vanilla-js-example) --- ## Folder Structure ``` vanilla-js-example/├── public/ # Static assets├── src/│ ├── components/ # JS UI components (auth, table, modal, banner, etc.)│ ├── main.js # Main entry point│ └── style.css # Tailwind CSS styles├── juno.config.mjs # Juno Satellite configuration├── package.json # Project dependencies and scripts├── vite.config.js # Vite build configuration├── README.md # User-facing documentation└── ... # Other config and build files ``` --- ## Key Features * **Juno Integration**: Uses Juno's Satellite for authentication, Datastore, and Storage. * **Authentication**: Login/logout flow. * **Notes Collection**: Users can create, view, and delete notes (text, with optional file URL). * **Images Collection**: Supports file storage for images. * **Responsive UI**: Built with Tailwind CSS for modern styling. * **Banner**: Warns if the Satellite is not configured for local development. --- ## Main Components * **src/main.js**: Main entry point, initializes Juno Satellite, handles authentication state. * **components/**: Contains UI and logic for authentication, notes table, modal, banner, etc. --- ## Data Structure * **Note** (used in table.js, modal.js, etc.): ``` // Example note object{ key: string, data: { text: string, url?: string }} ``` --- ## How to Run 1. **Install dependencies**: ``` npm install ``` 2. **Start Juno local emulator**: **Important:** Requires the Juno CLI to be available `npm i -g @junobuild/cli` ``` juno dev start ``` 3. **Create a Satellite** for local dev: * Visit [http://localhost:5866](http://localhost:5866) and follow the instructions. * Update `juno.config.mjs` with your Satellite ID. 4. **Create required collections**: * `notes` in Datastore: [http://localhost:5866/datastore](http://localhost:5866/datastore) * `images` in Storage: [http://localhost:5866/storage](http://localhost:5866/storage) 5. **Start the frontend dev server** (in a separate terminal): ``` npm run start ``` --- ## Juno-Specific Configuration * **juno.config.mjs**: Defines Satellite IDs for development/production, build source, and predeploy steps. --- ## Production Deployment * Create a Satellite on the [Juno Console](https://juno.build) for mainnet. * Update `juno.config.mjs` with the production Satellite ID. * Build and deploy: ``` npm run buildjuno deploy ``` --- ## Notes * The app is intended as a starting point for Juno-based projects. * All logic is in vanilla JavaScript modules. * The app is fully client-side (Server Side Rendering is not supported yet) and interacts with Juno via the Satellite container. --- ## Juno SDK Used The following functions from `@junobuild/core` are used in this example: | Function | Purpose/Description | Where Used (File/Component) | Juno Docs/Source Reference | | --- | --- | --- | --- | | `initSatellite` | Initialize Juno Satellite container | [`src/main.js`](https://github.com/junobuild/create-juno/blob/main/templates/vanilla-js-example/src/main.js) | [Initialization](/docs/setup-the-sdk.md#initialization) | | `authSubscribe` | Subscribe to auth state changes | [`src/main.js`](https://github.com/junobuild/create-juno/blob/main/templates/vanilla-js-example/src/main.js), [`src/components/modal.js`](https://github.com/junobuild/create-juno/blob/main/templates/vanilla-js-example/src/components/modal.js) | [Subscription](/docs/build/authentication/development.md#subscription) | | `signIn` | Sign in user | [`src/components/login.js`](https://github.com/junobuild/create-juno/blob/main/templates/vanilla-js-example/src/components/login.js) | [Sign-in](/docs/build/authentication/development.md#sign-in) | | `signOut` | Sign out user | [`src/components/logout.js`](https://github.com/junobuild/create-juno/blob/main/templates/vanilla-js-example/src/components/logout.js) | [Sign-out](/docs/build/authentication/development.md#sign-out) | | `listDocs` | List documents in a collection | [`src/components/table.js`](https://github.com/junobuild/create-juno/blob/main/templates/vanilla-js-example/src/components/table.js) | [List documents](/docs/build/datastore/development.md#list-documents) | | `setDoc` | Create or update a document | [`src/components/modal.js`](https://github.com/junobuild/create-juno/blob/main/templates/vanilla-js-example/src/components/modal.js) | [Add a document](/docs/build/datastore/development.md#add-a-document) | | `deleteDoc` | Delete a document | [`src/components/delete.js`](https://github.com/junobuild/create-juno/blob/main/templates/vanilla-js-example/src/components/delete.js) | [Delete a document](/docs/build/datastore/development.md#delete-a-document) | | `uploadFile` | Upload a file to storage | [`src/components/modal.js`](https://github.com/junobuild/create-juno/blob/main/templates/vanilla-js-example/src/components/modal.js) | [Upload asset](/docs/build/storage/development.md#upload-asset) | | `deleteAsset` | Delete a file from storage | [`src/components/delete.js`](https://github.com/junobuild/create-juno/blob/main/templates/vanilla-js-example/src/components/delete.js) | [Delete asset](/docs/build/storage/development.md#delete-asset) | # Vue Example This project is a note-taking app template built with **Vue**, **TypeScript**, and **Tailwind CSS**, designed to demonstrate integration with Juno for app development. It showcases authentication, data storage, and file storage using Juno's Satellite container. You can scaffold it using the following command, or browse the source code: ``` npm create juno@latest -- --template vue-example ``` Source: [github.com/junobuild/create-juno/templates/vue-example](https://github.com/junobuild/create-juno/tree/main/templates/vue-example) --- ## Folder Structure ``` vue-example/├── public/ # Static assets├── src/│ ├── components/ # Vue UI components (Auth, Table, Modal, Banner, etc.)│ ├── stores/ # Pinia stores for auth, etc.│ ├── types/ # TypeScript types (e.g., note.ts)│ ├── App.vue # Main app component│ ├── main.ts # Vue entry point├── juno.config.ts # Juno Satellite configuration├── package.json # Project dependencies and scripts├── vite.config.ts # Vite build configuration├── README.md # User-facing documentation└── ... # Other config and build files ``` --- ## Key Features * **Juno Integration**: Uses Juno's Satellite for authentication, Datastore, and Storage. * **Authentication**: Login/logout flow. * **Notes Collection**: Users can create, view, and delete notes (text, with optional file URL). * **Images Collection**: Supports file storage for images. * **Responsive UI**: Built with Tailwind CSS for modern styling. * **Banner**: Warns if the Satellite is not configured for local development. --- ## Main Components * **src/App.vue**: Main app component, initializes Juno Satellite, wraps content in authentication. * **components/**: Contains UI and logic for authentication, notes table, modal, banner, etc. * **stores/auth.store.ts**: Pinia store for authentication state. * **types/note.ts**: TypeScript interface for notes. --- ## Data Structure * **Note** (`src/types/note.ts`): ``` export interface Note { text: string; url?: string;} ``` --- ## How to Run 1. **Install dependencies**: ``` npm install ``` 2. **Start Juno local emulator**: **Important:** Requires the Juno CLI to be available `npm i -g @junobuild/cli` ``` juno dev start ``` 3. **Create a Satellite** for local dev: * Visit [http://localhost:5866](http://localhost:5866) and follow the instructions. * Update `juno.config.ts` with your Satellite ID. 4. **Create required collections**: * `notes` in Datastore: [http://localhost:5866/datastore](http://localhost:5866/datastore) * `images` in Storage: [http://localhost:5866/storage](http://localhost:5866/storage) 5. **Start the frontend dev server** (in a separate terminal): ``` npm run start ``` --- ## Juno-Specific Configuration * **juno.config.ts**: Defines Satellite IDs for development/production, build source, and predeploy steps. --- ## Production Deployment * Create a Satellite on the [Juno Console](https://juno.build) for mainnet. * Update `juno.config.ts` with the production Satellite ID. * Build and deploy: ``` npm run buildjuno deploy ``` --- ## Notes * The app is intended as a starting point for Juno-based projects. * All logic is in TypeScript and Vue components/stores. * The app is fully client-side (Server Side Rendering is not supported yet) and interacts with Juno via the Satellite container. --- ## Juno SDK Used The following functions from `@junobuild/core` are used in this example: | Function | Purpose/Description | Where Used (File/Component) | Juno Docs/Source Reference | | --- | --- | --- | --- | | `initSatellite` | Initialize Juno Satellite container | [`src/App.vue`](https://github.com/junobuild/create-juno/blob/main/templates/vue-example/src/App.vue) | [Initialization](/docs/setup-the-sdk.md#initialization) | | `authSubscribe` | Subscribe to auth state changes | [`src/stores/auth.store.ts`](https://github.com/junobuild/create-juno/blob/main/templates/vue-example/src/stores/auth.store.ts) | [Subscription](/docs/build/authentication/development.md#subscription) | | `signIn` | Sign in user | [`src/components/Login.vue`](https://github.com/junobuild/create-juno/blob/main/templates/vue-example/src/components/Login.vue) | [Sign-in](/docs/build/authentication/development.md#sign-in) | | `signOut` | Sign out user | [`src/components/Logout.vue`](https://github.com/junobuild/create-juno/blob/main/templates/vue-example/src/components/Logout.vue) | [Sign-out](/docs/build/authentication/development.md#sign-out) | | `listDocs` | List documents in a collection | [`src/components/Table.vue`](https://github.com/junobuild/create-juno/blob/main/templates/vue-example/src/components/Table.vue) | [List documents](/docs/build/datastore/development.md#list-documents) | | `setDoc` | Create or update a document | [`src/components/Modal.vue`](https://github.com/junobuild/create-juno/blob/main/templates/vue-example/src/components/Modal.vue) | [Add a document](/docs/build/datastore/development.md#add-a-document) | | `deleteDoc` | Delete a document | [`src/components/Delete.vue`](https://github.com/junobuild/create-juno/blob/main/templates/vue-example/src/components/Delete.vue) | [Delete a document](/docs/build/datastore/development.md#delete-a-document) | | `uploadFile` | Upload a file to storage | [`src/components/Modal.vue`](https://github.com/junobuild/create-juno/blob/main/templates/vue-example/src/components/Modal.vue) | [Upload asset](/docs/build/storage/development.md#upload-asset) | | `deleteAsset` | Delete a file from storage | [`src/components/Delete.vue`](https://github.com/junobuild/create-juno/blob/main/templates/vue-example/src/components/Delete.vue) | [Delete asset](/docs/build/storage/development.md#delete-asset) | # Using Juno with AI If you’re using AI to build with Juno, you can use our `llms.txt` files to help AI tools better understand the platform. --- ## LLMs.txt An [LLMs.txt](https://llmstxt.org/) file is a plain text file that provides instructions or metadata for large language models (LLMs). It often specifies how LLMs should process or interact with content. It's similar to a `robots.txt` or `sitemap.xml` file, but tailored for AI models. ### Available routes We provide several `llms.txt` routes. Use the one that works best with your AI tool: * [`llms.txt`](/llms.txt): Table of contents with links to individual Markdown docs * [`llms-full.txt`](/llms-full.txt): Entire documentation in a single Markdown file ### How to use it Here are some examples of how the `llms.txt` files can be used with AI tools. **Note:** 🙏 Help us improve! If you use a tool that supports LLMs.txt files, [open a pull request](https://github.com/junobuild/docs/edit/main/docs/guides/ai.md) to add your example to this page. ### Cursor You can add custom documentation as context in Cursor using the `@Docs` feature or by navigating to `Cursor Settings` > `Features` > `Docs`. [Read more about it here](https://docs.cursor.com/context/@-symbols/@-docs). # Use Juno with Angular Explore how to create a Juno project developed with Angular. What would you like to do? ([Build](#build))([Hosting](#hosting)) **Options:** Choose **Build** if you want to build a full featured rich application. Choose **Hosting** if you just want to deploy a website. --- ## Build Ready to implement a feature-rich application with Juno? You can choose a step-by-step approach, building each component gradually, or dive into our quickstart template, which showcases Juno's core features. Which path would you like to explore next? ([Step-by-step](#step-by-step))([Quickstart](#quickstart)) --- ### Step-by-step This guide provides quickstart instructions for integrating Juno in two scenarios: starting a new project and adding Juno to an existing Angular app. #### 1\. Choose Your Integration Path You can either start a new project or add Juno to an existing app. ##### Path A: Start a new project with a template Create a new project using the Juno quickstart CLI: * npm * yarn * pnpm ``` npm create juno@latest -- --template angular-starter ``` ``` yarn create juno -- --template angular-starter ``` ``` pnpm create juno -- --template angular-starter ``` ##### Path B: Integrate Juno into an existing Angular app Navigate to your existing app: ``` cd your-existing-app ``` and install Juno SDK: * npm * yarn * pnpm ``` npm i @junobuild/core ``` ``` yarn add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` ``` pnpm add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` #### 2\. Start the Emulator If the Juno admin CLI (required to run the emulator) is not installed yet, run: ``` npm i -g @junobuild/cli ``` Once installed, start the local emulator: ``` juno dev start ``` Open the Console UI at [http://localhost:5866/](http://localhost:5866/). **Note:** When developing locally, you get an all-in-one emulator that closely mimics the production environment. This includes providing Juno and its Console UI locally. Sign in, create a Satellite, navigate to the **Datastore** section, and create a collection named **demo**. #### 3\. Configure To initialize your project with the Satellite ID you created, configure it in the `juno.config.mjs` file (or other extension), which should be available at the root. Replace `` with the ID. ``` import { defineConfig } from "@junobuild/config";/** @type {import('@junobuild/config').JunoConfig} */export default defineConfig({ satellite: { ids: { development: "", production: "" }, source: "out", predeploy: ["npm run build"] }}); ``` In addition, add also the ID to your `environment.ts` file: ``` export const environment = { satelliteId: ""}; ``` #### 4\. Insert data from your app In `app.component.ts`, initialize the Satellite. Add an `insert` function to persist a document. app.component.ts ``` import { Component } from "@angular/core";import { type Doc, initSatellite, setDoc } from "@junobuild/core";import { environment } from "../environments/environment";@Component({ selector: "app-root", template: ` Key: {{ doc.key }} `, styleUrls: ["./app.component.css"]})export class AppComponent { doc: Doc<{ hello: string }> | undefined = undefined; async ngOnInit() { await initSatellite({ satelliteId: environment.satelliteId }); } async insert() { this.doc = await setDoc({ collection: "demo", doc: { key: window.crypto.randomUUID(), data: { hello: "world" } } }); }} ``` #### 5\. Start the app Start the app, go to [http://localhost:4200](http://localhost:4200) in a browser, click "Insert a document," and you should see the data successfully persisted in your satellite on the blockchain. **What's Next: Going Live:** Once you're ready to deploy your app for others to access, continue to the ([Deployment guide](#deployment)). --- ### Quickstart This example demonstrates how to quickly deploy a basic note-taking app that integrates Juno's core features: * [Authentication](/docs/build/authentication.md): easy-to-use SDKs that support truly anonymous authentication. * [Datastore](/docs/build/datastore.md): a simple key-pair database for storing user data and other information. * [Storage](/docs/build/storage.md): a file storage system to store and serve user-generated content, such as photos. Using the Juno CLI, you can easily scaffold this app. * npm * yarn * pnpm ``` npm create juno@latest -- --template angular-example ``` ``` yarn create juno -- --template angular-example ``` ``` pnpm create juno -- --template angular-example ``` Follow the CLI prompts to choose the note-taking app example and select local development. The CLI will manage all configurations and dependencies, allowing you to focus on exploring and customizing your app right away. --- ## Hosting If you already have an [Angular](https://angular.dev/) app, you're all set — proceed to the ([Deployment](#deployment)) section to upload your project to production. Otherwise, you can bootstrap a new website using the Juno template by running the following command: * npm * yarn * pnpm ``` npm create juno@latest -- --template angular-starter ``` ``` yarn create juno -- --template angular-starter ``` ``` pnpm create juno -- --template angular-starter ``` Once you’re set up, continue to the ([Deployment](#deployment)) section below. --- ## Deployment Use this guide to deploy your project to production — directly to a smart contract on mainnet. **Note:** When following the steps below, make sure to: 1. Register the `satelliteId` in the environment: When you create the satellite using juno build, a `satelliteId` is generated. This `satelliteId` must be copied into the `src/environments/environment.prod.ts` file. 2. Configure the dist folder correctly: In Angular, the build artifacts are not always placed directly under the `dist` folder. Instead, they may be located at `dist/` or `dist//browser`. When running `juno init`, you need to provide the full path to ensure that `juno deploy` works as expected. ### 1. Set up a satellite If you haven't created a satellite yet, go ahead and [create](/docs/create-a-satellite.md) a new one in the Juno's console. ### 2. Deploy Once your satellite is up and running, you can proceed with uploading your app to your smart contract. You can either automate your deployment with GitHub Actions (recommended) or deploy manually from your device. Choose your method: [GitHub Actions](/docs/guides/github-actions.md)[Manual](/docs/guides/manual-deployment.md) # Use Juno with Astro Easily set up and deploy your Astro project with Juno. What would you like to do? ([Build](#build))([Hosting](#hosting)) **Options:** Choose **Build** if you want to build a full featured rich application. Choose **Hosting** if you just want to deploy a website. --- ## Build This guide provides quickstart instructions for integrating Juno and building a feature-rich application. It also includes guidance on developing locally. ### 1\. Choose Your Integration Path You can either start a new project or add Juno to an existing app. #### Path A: Start a new project with a template Create a new project using the Juno quickstart CLI: * npm * yarn * pnpm ``` npm create juno@latest -- --template astro-starter ``` ``` yarn create juno -- --template astro-starter ``` ``` pnpm create juno -- --template astro-starter ``` #### Path B: Integrate Juno into an existing Astro app Navigate to your existing app: ``` cd your-existing-app ``` and install Juno SDK: * npm * yarn * pnpm ``` npm i @junobuild/core ``` ``` yarn add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` ``` pnpm add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` ### 2\. Start the Emulator If the Juno admin CLI (required to run the emulator) is not installed yet, run: ``` npm i -g @junobuild/cli ``` Once installed, start the local emulator: ``` juno dev start ``` Open the Console UI at [http://localhost:5866/](http://localhost:5866/). **Note:** When developing locally, you get an all-in-one emulator that closely mimics the production environment. This includes providing Juno and its Console UI locally. Sign in, create a Satellite, navigate to the **Datastore** section, and create a collection named **demo**. ### 3\. Configure To initialize the library with the Satellite ID you created, configure it in the `juno.config.mjs` file (or other extension), which should be available at the root of your project. Replace `` with the ID. ``` import { defineConfig } from "@junobuild/config";/** @type {import('@junobuild/config').JunoConfig} */export default defineConfig({ satellite: { ids: { development: "", production: "" }, source: "out", predeploy: ["npm run build"] }}); ``` ### 4\. Install the Plugin You'll need to install the plugin to automatically inject the Satellite ID into your app as an environment variable. Proceed as documented [here](/docs/reference/plugins.md#vite-plugin). astro.config.mjs ``` import { defineConfig } from "astro/config";import juno from "@junobuild/vite-plugin";import sitemap from "@astrojs/sitemap";// https://astro.build/configexport default defineConfig({ site: "https://hello.com", integrations: [sitemap()], vite: { plugins: [juno()] }, devToolbar: { enabled: false }}); ``` ### 5\. Insert data from your app In `index.astro`, initialize the Satellite. Add an `insert` function to persist a document. index.astro ```

Document persisted key:

``` ### 6\. Start the app Start the app, go to [http://localhost:4321/](http://localhost:4321/) in a browser, click "Insert a document", and you should see the data successfully persisted in your satellite on the blockchain. **What's Next: Going Live:** Once you're ready to deploy your app for others to access, continue to the ([Deployment guide](#deployment)). --- ## Hosting If you already have an [Astro](https://astro.build/) app, you're all set — proceed to the ([Deployment](#deployment)) section to upload your project to production. Otherwise, you can bootstrap a new website using the Juno template by running the following command: * npm * yarn * pnpm ``` npm create juno@latest -- --template astro-starter ``` ``` yarn create juno -- --template astro-starter ``` ``` pnpm create juno -- --template astro-starter ``` Once you’re set up, continue to the ([Deployment](#deployment)) section below. --- ## Deployment Use this guide to deploy your project to production — directly to a smart contract on mainnet. ### 1. Set up a satellite If you haven't created a satellite yet, go ahead and [create](/docs/create-a-satellite.md) a new one in the Juno's console. ### 2. Deploy Once your satellite is up and running, you can proceed with uploading your app to your smart contract. You can either automate your deployment with GitHub Actions (recommended) or deploy manually from your device. Choose your method: [GitHub Actions](/docs/guides/github-actions.md)[Manual](/docs/guides/manual-deployment.md) # Local Development Juno offers something most platforms don’t: a full local development environment that closely mirrors production. When you develop locally, you're running an emulator that includes the well known infrastructure services — including the actual administration Console UI. This enables: * A development experience that mirrors mainnet, helping you build with confidence * A smooth dev loop, from prototype to deployment * A unique way to build, debug, and validate smart contract logic and frontend behavior — all in one place ![A screenshot of the DEV Console UI login screen](/assets/images/login-c6e898b09eac53a54e6f2d2e2b27e03e.webp) --- ## Before you begin An emulator — a self-contained local environment that includes services, replicas, and the Console UI — is run using containers. You can use [Docker](https://www.docker.com/) (default) or [Podman](https://podman.io/) as the container runtime. Make sure either tool is installed on your machine: * [Docker: Windows](https://docs.docker.com/desktop/install/windows-install/) * [Docker: macOS](https://docs.docker.com/desktop/install/mac-install/) * [Docker: Linux](https://docs.docker.com/desktop/install/linux-install/) * [Podman: Installation guide](https://podman.io/getting-started/installation) **Important:** For MacBooks with M-series processors, if you aim to use **Docker**, it is important to install Docker Desktop **version 4.25.0 or later**, ideally the latest available version. For **Podman**, we are not aware of any particular version requirements at this time. --- ## Getting Started The easiest way to run the local developer environment is through the CLI. If you haven’t installed it yet, run: * npm * yarn * pnpm ``` npm i -g @junobuild/cli ``` ``` yarn global add @junobuild/cli ``` ``` pnpm add -g @junobuild/cli ``` Then, in your project folder, start the local emulator with: ``` juno dev start ``` This will launch the emulator along with all the services needed to develop your project. We recommend running this in a dedicated terminal window or tab, while your frontend project (e.g. using Vite or Next.js) runs separately using npm run dev or similar. To stop the emulator, run: ``` juno dev stop ``` --- ## Available Images Juno supports two main environments for running your project locally, each tailored to different use cases. | Image | Description | Includes Console UI | Best for | | --- | --- | --- | --- | | `junobuild/skylab` | All-in-one local environment like production | ✅ | End-to-end dev, exploration | | `junobuild/satellite` | Lightweight setup running a single Satellite | ❌ | CI, focused app testing | Both variants run on a local network, services and support live-reloading for serverless functions written in Rust or TypeScript. * Use **Skylab** for the full experience, including the Console UI and supporting infrastructure. * Use **Satellite** when you want a faster, minimal setup for a specific app or automated tests. The table below shows which modules are available in each image and helps clarify what’s included when running locally with Skylab or Satellite. | Module | Skylab ✅ | Satellite ✅ | | --- | --- | --- | | Console (Backend) | ✅ | ❌ | | Console (UI) | ✅ | ❌ | | Create Satellites / Orbiters via Console UI | ✅ | ❌ | | Default (auto-deployed) Satellite | ❌ | ✅ | | Observatory | ✅ | ❌ | | Internet Identity | ✅ | ✅ | | ICP Ledger | ✅ | ✅ | | ICP Index | ✅ | ✅ | | NNS Governance | ✅ | ❌ | | Cycles Minting (CMC) | ✅ | ❌ | **Note:** The default (auto-deployed) Satellite is available with a predefined canister ID `jx5yt-yyaaa-aaaal-abzbq-cai`. --- ## Console UI When using the `junobuild/skylab` image, the Console UI becomes available by default at: ``` http://localhost:5866/ ``` Once the emulator is running (`juno dev start`), visit this URL in your browser to explore the Console — where you can create and manage Satellites, and explore features like Datastore, Authentication, Storage, and more. --- ## Hot Reload The local container supports live reloading. When you modify your [configuration](/docs/reference/emulator/satellite.md#configuration) or build custom [Functions](/docs/build/functions.md) to enhance Juno's capabilities with serverless features, those changes will be automatically redeployed. --- ## Configuration Options To customize the behavior of the local emulator—such as changing ports, setting a persistent volume name, or overriding the runner image — refer to the [Emulator Configuration](/docs/reference/configuration.md#emulator-configuration) section. There you'll find detailed information about available options including: * ⚙️ Runner settings (e.g. image, platform, volume) * 🔌 Custom port mappings * 📁 Shared folders and hot reloading * 🧪 CI and test environment tips --- ## Usage During local development, your app connects to the local emulator (container) by default — no extra configuration needed. This is handled automatically when using the [plugins](/docs/reference/plugins.md), or when starting from a template. If needed, you can opt out of the container behavior by explicitly setting `container: false`. ### Manual Initialization If you're not using a plugin and are initializing Juno manually, here's how to configure it to use the local container: ``` import { initSatellite } from "@junobuild/core";const container = import.meta.env.DEV === true;await initSatellite({ satelliteId: container ? "jx5yt-yyaaa-aaaal-abzbq-cai" : "aaaaa-bbbbb-ccccc-ddddd-cai", container}); ``` The SDK will automatically detect the container in local development. If you want to disable that behavior and connect directly to a remote canister (e.g. in CI or production testing), you can do: ``` await initSatellite({ satelliteId: "aaaaa-bbbbb-ccccc-ddddd-cai", container: false}); ``` --- ## Administration The admin server running on port `5999` provides a variety of internal management. Below are some tips and example scripts to make use of this little server. ### Get ICP If you're using the full environment, the Console UI includes a "Get ICP" button in the wallet. It’s a quick way to get ICP out of the box. ![A screenshot of the wallet with the Get ICP call to action of Console UI in dev mode](/assets/images/wallet-45c84f1968e0c6a2749b439aace1b0c1.webp) You might want to transfer some ICP from the ledger to a specified principal, which can be particularly useful when you're just getting started developing your app and no users currently own ICP. This can be achieved by querying: ``` http://localhost:5999/ledger/transfer/?to=$PRINCIPAL ``` For example, you can use the following script: ``` #!/usr/bin/env bash# Check if a principal is passed as an argument; otherwise, prompt for itif [ -z "$1" ]; then read -r -p "Enter the Wallet ID (owner account, principal): " PRINCIPALelse PRINCIPAL=$1fi# Make a transfer request to the admin servercurl "http://localhost:5999/ledger/transfer/?to=$PRINCIPAL" ``` # Manual Deployment We recommend using [GitHub Actions](/docs/guides/github-actions.md) for automated and efficient deployments. However, this guide walks you through manually deploying your app using the Juno CLI, covering the setup, build, and deployment process to your Juno Satellite. --- ## 1\. Install Juno CLI Install the Juno command line interface by executing the following command in your terminal: * npm * yarn * pnpm ``` npm i -g @junobuild/cli ``` ``` yarn global add @junobuild/cli ``` ``` pnpm add -g @junobuild/cli ``` --- ## 2\. Initialization Once the CLI is set up, initialize your project by running: ``` juno init ``` This command generates a configuration file and prompts you to log in to your Satellite from the terminal to authenticate your device. Upon execution, Juno’s Console will open in your browser, where you’ll be asked to grant permissions for your modules. These include Mission Control (your wallet), Satellite(s), and Analytics, allowing secure access from your machine. **Info:** If the login process opens in a different browser than the one where you're already signed into, simply copy the URL and paste it into your authenticated browser to continue. This can occur if you've signed into the Juno Console in a browser other than your system's default. --- ## 3\. Deploy Your Project You can use the CLI to manually deploy different parts of your app: * 🪄 Deploy frontend assets to your Satellite. Learn how. * 🛠️ Build, publish and upgrade serverless functions (TypeScript or Rust). Learn how. --- ### a) 🪄 Deploy Frontend Assets Get your app ready for deployment: * npm * yarn * pnpm ``` npm run build ``` ``` yarn build ``` ``` pnpm build ``` Deploy your application or website by running the following command from your project’s root folder: ``` juno deploy ``` **Tip:** When prompted for the name or path of the folder containing your built dapp files, provide the appropriate folder name for your framework, such as `build` (SvelteKit), `out` (Next.js), or `dist` (React, Astro, or Vue). Wait for the deploy to complete. Once uploaded, it will be live on your Juno Satellite and accessible on the web. --- ### b) 🛠️ Build, Publish, and Upgrade Serverless Functions To build and deploy your serverless functions written in TypeScript or Rust, you can use the CLI. ``` juno functions buildjuno functions publishjuno functions upgrade ``` * Use publish `--no-apply` if your access key only has a **submit** role and cannot directly upgrade. * You can then approve and apply the change using the CLI (with another key) or Console UI. For a full overview of the serverless lifecycle—including setup, development, local testing, publishing, approvals, and upgrades — see the Serverless Functions Lifecycle guide. # Use Juno with Next.js Explore how to create a Juno project developed with Next.js. What would you like to do? ([Build](#build))([Hosting](#hosting)) **Options:** Choose **Build** if you want to build a full featured rich application. Choose **Hosting** if you just want to deploy a website. --- ## Build Ready to implement a feature-rich application with Juno? You can choose a step-by-step approach, building each component gradually, or dive into our quickstart template, which showcases Juno's core features. Which path would you like to explore next? ([Step-by-step](#step-by-step))([Quickstart](#quickstart)) --- ### Step-by-step This guide provides quickstart instructions for integrating Juno in two scenarios: starting a new project and adding Juno to an existing Next.js app. #### 1\. Choose Your Integration Path You can either start a new project or add Juno to an existing app. ##### Path A: Start a new project with a template Create a new project using the Juno quickstart CLI: * npm * yarn * pnpm ``` npm create juno@latest -- --template nextjs-starter ``` ``` yarn create juno -- --template nextjs-starter ``` ``` pnpm create juno -- --template nextjs-starter ``` ##### Path B: Integrate Juno into an existing Next.js app Navigate to your existing app: ``` cd your-existing-app ``` and install Juno SDK: * npm * yarn * pnpm ``` npm i @junobuild/core ``` ``` yarn add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` ``` pnpm add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` #### 2\. Start the Emulator If the Juno admin CLI (required to run the emulator) is not installed yet, run: ``` npm i -g @junobuild/cli ``` Once installed, start the local emulator: ``` juno dev start ``` Open the Console UI at [http://localhost:5866/](http://localhost:5866/). **Note:** When developing locally, you get an all-in-one emulator that closely mimics the production environment. This includes providing Juno and its Console UI locally. Sign in, create a Satellite, navigate to the **Datastore** section, and create a collection named **demo**. #### 3\. Configure To initialize the library with the Satellite ID you created, configure it in the `juno.config.mjs` file (or other extension), which should be available at the root of your project. Replace `` with the ID. ``` import { defineConfig } from "@junobuild/config";/** @type {import('@junobuild/config').JunoConfig} */export default defineConfig({ satellite: { ids: { development: "", production: "" }, source: "out", predeploy: ["npm run build"] }}); ``` #### 4\. Install the Plugin (If Needed) If you didn't start your project from a template, you'll need to install the plugin to automatically inject the Satellite ID into your app as an environment variable. Proceed as documented [here](/docs/reference/plugins.md#nextjs-plugin). #### 5\. Insert data from your app In `Page.tsx`, (if using TypeScript) or the corresponding JavaScript file, initialize the Satellite. Add an `insert` function to persist a document as well. Page.tsx ``` "use client";import { useEffect, useState } from "react";import { type Doc, initSatellite, setDoc } from "@junobuild/core-peer";type Record = { hello: string;};export default function Home() { const [record, setRecord] = useState | undefined>(undefined); useEffect(() => { (async () => await initSatellite())(); }, []); const insert = async () => { const doc = await setDoc({ collection: "demo", doc: { key: window.crypto.randomUUID(), data: { hello: "world", }, }, }); setRecord(doc); }; return ( <> {record !== undefined && Key: {record.key}} );} ``` #### 6\. Start the app Start the app and go to [http://localhost:3000](http://localhost:3000) in a browser. Click "Insert a document" to see the data successfully persisted in your satellite on the blockchain. **What's Next: Going Live:** Once you're ready to deploy your app for others to access, continue to the ([Deployment guide](#deployment)). --- ### Quickstart This example demonstrates how to quickly deploy a basic note-taking app that integrates Juno's core features: * [Authentication](/docs/build/authentication.md): easy-to-use SDKs that support truly anonymous authentication. * [Datastore](/docs/build/datastore.md): a simple key-pair database for storing user data and other information. * [Storage](/docs/build/storage.md): a file storage system to store and serve user-generated content, such as photos. Using the Juno CLI, you can easily scaffold this app. * npm * yarn * pnpm ``` npm create juno@latest -- --template nextjs-example ``` ``` yarn create juno -- --template nextjs-example ``` ``` pnpm create juno -- --template nextjs-example ``` Follow the CLI prompts to choose the note-taking app example and select local development. The CLI will manage all configurations and dependencies, allowing you to focus on exploring and customizing your app right away. --- ## Hosting If you already have an [Next.js](https://nextjs.org/) app, you're all set — proceed to the ([Deployment](#deployment)) section to upload your project to production. Otherwise, you can bootstrap a new website using the Juno template by running the following command: * npm * yarn * pnpm ``` npm create juno@latest -- --template nextjs-starter ``` ``` yarn create juno -- --template nextjs-starter ``` ``` pnpm create juno -- --template nextjs-starter ``` Once you’re set up, continue to the ([Deployment](#deployment)) section below. --- ## Deployment Use this guide to deploy your project to production — directly to a smart contract on mainnet. ### 1\. Static exports The Internet Computer, including Juno, currently does not support Server Side Rendering (_without workaround_). Therefore, it is recommended to generate a pre-rendered or client-side-only frontend application. We suggest using the [static exports](https://nextjs.org/docs/pages/building-your-application/deploying/static-exports) option from Next.js. In `next.config.js` file: next.config.js ``` /** @type {import('next').NextConfig} */const nextConfig = { output: "export"};module.exports = nextConfig; ``` ### 2. Set up a satellite If you haven't created a satellite yet, go ahead and [create](/docs/create-a-satellite.md) a new one in the Juno's console. ### 3. Deploy Once your satellite is up and running, you can proceed with uploading your app to your smart contract. You can either automate your deployment with GitHub Actions (recommended) or deploy manually from your device. Choose your method: [GitHub Actions](/docs/guides/github-actions.md)[Manual](/docs/guides/manual-deployment.md) # Use Juno in a NodeJS context This guide is intended for use of Juno from a client running in a non-interactive environment, i.e. not in a browser. **Tip:** You can find a few examples of NodeJS usage in the [example](https://github.com/junobuild/examples/tree/main/node) repository. --- ## Usage To get started, you need to ensure the SDK is installed in your project: * npm * yarn * pnpm ``` npm i @junobuild/core ``` ``` yarn add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` ``` pnpm add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` Unlike in a web application, initializing Juno globally in your NodeJS app is not required. Instead, you can call any of Juno's functions with an additional parameter that includes the satellite ID and other parameters, such as `container` set to `true` if you are [developing locally](/docs/guides/local-development.md). Moreover, since no interactive [Authentication](/docs/build/authentication.md) is performed in this context, the information must also be provided. The DFINITY [agent-js](https://github.com/dfinity/agent-js/) library can be used to build an `identity`. --- ## Example To call the `getDoc` function from the Datastore: ``` import { getDoc } from "@junobuild/core-peer";import { AnonymousIdentity } from "@dfinity/agent";const satellite = { identity: new AnonymousIdentity(), id: "jx5yt-yyaaa-aaaal-abzbq-cai", container: true};const doc = await getDoc({ collection: "demo", key: "id2", satellite});console.log(doc); ``` # Use Juno with React Explore how to create a Juno project developed with React. What would you like to do? ([Build](#build))([Hosting](#hosting)) **Options:** Choose **Build** if you want to build a full featured rich application. Choose **Hosting** if you just want to deploy a website. --- ## Build Ready to implement a feature-rich application with Juno? You can choose a step-by-step approach, building each component gradually, or dive into our quickstart template, which showcases Juno's core features. Which path would you like to explore next? ([Step-by-step](#step-by-step))([Quickstart](#quickstart)) --- ### Step-by-step This guide provides quickstart instructions for integrating Juno in two scenarios: starting a new project and adding Juno to an existing React app. #### 1\. Choose Your Integration Path You can either start a new project or add Juno to an existing app. ##### Path A: Start a new project with a template Create a new project using the Juno quickstart CLI: * npm * yarn * pnpm ``` npm create juno@latest -- --template react-starter ``` ``` yarn create juno -- --template react-starter ``` ``` pnpm create juno -- --template react-starter ``` ##### Path B: Integrate Juno into an existing React app Navigate to your existing app: ``` cd your-existing-app ``` and install Juno SDK: * npm * yarn * pnpm ``` npm i @junobuild/core ``` ``` yarn add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` ``` pnpm add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` #### 2\. Start the Emulator If the Juno admin CLI (required to run the emulator) is not installed yet, run: ``` npm i -g @junobuild/cli ``` Once installed, start the local emulator: ``` juno dev start ``` Open the Console UI at [http://localhost:5866/](http://localhost:5866/). **Note:** When developing locally, you get an all-in-one emulator that closely mimics the production environment. This includes providing Juno and its Console UI locally. Sign in, create a Satellite, navigate to the **Datastore** section, and create a collection named **demo**. #### 3\. Configure To initialize the library with the Satellite ID you created, configure it in the `juno.config.ts` file (or other extension), which should be available at the root of your project. Replace `` with the ID. ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { ids: { development: "", production: "" }, source: "dist", predeploy: ["npm run build"] }}); ``` #### 4\. Install the Plugin (If Needed) If you didn't start your project from a template, you'll need to install the plugin to automatically inject the Satellite ID into your app as an environment variable. Proceed as documented [here](/docs/reference/plugins.md#vite-plugin). #### 5\. Insert data from your app In `App.jsx`, initialize the library with the satellite. Add an `insert` function to persist a document. App.jsx ``` import { useEffect, useState } from "react";import { initSatellite, setDoc } from "@junobuild/core";function App() { const [record, setRecord] = useState(undefined); useEffect(() => { (async () => await initSatellite())(); }, []); const insert = async () => { const doc = await setDoc({ collection: "demo", doc: { key: window.crypto.randomUUID(), data: { hello: "world" } } }); setRecord(doc); }; return ( <> {record !== undefined && Key: {record.key}} );}export default App; ``` #### 6\. Start the app Start the app and go to [http://localhost:5173](http://localhost:5173) in a browser. Click "Insert a document" to see the data successfully persisted in your satellite on the blockchain. **What's Next: Going Live:** Once you're ready to deploy your app for others to access, continue to the ([Deployment guide](#deployment)). --- ### Quickstart This example demonstrates how to quickly deploy a basic note-taking app that integrates Juno's core features: * [Authentication](/docs/build/authentication.md): easy-to-use SDKs that support truly anonymous authentication. * [Datastore](/docs/build/datastore.md): a simple key-pair database for storing user data and other information. * [Storage](/docs/build/storage.md): a file storage system to store and serve user-generated content, such as photos. Using the Juno CLI, you can easily scaffold this app. * npm * yarn * pnpm ``` npm create juno@latest -- --template react-example ``` ``` yarn create juno -- --template react-example ``` ``` pnpm create juno -- --template react-example ``` Follow the CLI prompts to choose the note-taking app example and select local development. The CLI will manage all configurations and dependencies, allowing you to focus on exploring and customizing your app right away. --- ## Hosting If you already have an [React](https://react.dev/) app, you're all set — proceed to the ([Deployment](#deployment)) section to upload your project to production. Otherwise, you can bootstrap a new website using the Juno template by running the following command: * npm * yarn * pnpm ``` npm create juno@latest -- --template react-starter ``` ``` yarn create juno -- --template react-starter ``` ``` pnpm create juno -- --template react-starter ``` Once you’re set up, continue to the ([Deployment](#deployment)) section below. --- ## Deployment Use this guide to deploy your project to production — directly to a smart contract on mainnet. ### 1. Set up a satellite If you haven't created a satellite yet, go ahead and [create](/docs/create-a-satellite.md) a new one in the Juno's console. ### 2. Deploy Once your satellite is up and running, you can proceed with uploading your app to your smart contract. You can either automate your deployment with GitHub Actions (recommended) or deploy manually from your device. Choose your method: [GitHub Actions](/docs/guides/github-actions.md)[Manual](/docs/guides/manual-deployment.md) # Code Functions in Rust Learn how to develop, integrate, and extend Juno Satellites with serverless functions written in Rust. --- ## Quickstart Set up your environment to develop and extend a Satellite with custom serverless functions. First, ensure you have the Juno CLI installed. If you haven't installed it yet, run: * npm * yarn * pnpm ``` npm i -g @junobuild/cli ``` ``` yarn global add @junobuild/cli ``` ``` pnpm add -g @junobuild/cli ``` At the root of your application, eject the Satellite if you haven't already used a template. ``` juno dev eject ``` In a new terminal window, kick off the emulator: ``` 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 ``` Changes are detected and automatically deployed, allowing you to test your custom code locally almost immediately. --- ## Hooks and Data Operations Serverless Functions are triggered by hooks, which respond to events occurring in the Satellite, such as setting a document. Before implementing a hook that manipulates data ("backend"), let's first set up a JavaScript function in your ("frontend") dApp. Define a setter function in your frontend dApp as follows: ``` interface Example { hello: string;}let key: string | undefined;const set = async () => { key = crypto.randomUUID(); const record = await setDoc({ collection: "demo", doc: { key, data: { hello: "world" } } }); console.log("Set done", record);}; ``` This code generates a key and persists a document in a collection of the Datastore named "demo". Additionally, add a getter to your code: ``` const get = async () => { if (key === undefined) { return; } const record = await getDoc({ collection: "demo", key }); console.log("Get done", record);}; ``` Without a hook, executing these two operations one after the other would result in a record containing "hello: world". Now, let's create a hook within `src/satellite/src/lib.rs` with the following implementation: ``` use ic_cdk::print;use junobuild_macros::{ on_delete_asset, on_delete_doc, on_delete_many_assets, on_delete_many_docs, on_set_doc, on_set_many_docs, on_upload_asset,};use junobuild_satellite::{ include_satellite, set_doc_store, OnDeleteAssetContext, OnDeleteDocContext, OnDeleteManyAssetsContext, OnDeleteManyDocsContext, OnSetDocContext, OnSetManyDocsContext, OnUploadAssetContext, SetDoc,};use junobuild_utils::{decode_doc_data, encode_doc_data};use serde::{Deserialize, Serialize};// The data of the document we are looking to update in the Satellite's Datastore.#[derive(Serialize, Deserialize)]struct Person { hello: String,}// We tells the hooks that we only want to listen to changes in collection "demo".#[on_set_doc(collections = ["demo"])]async fn on_set_doc(context: OnSetDocContext) -> Result<(), String> { // We decode the new data saved in the Datastore because it holds those as blob. let mut data: Person = decode_doc_data(&context.data.data.after.data)?; // We update the document's data that was saved in the Datastore with the call from the frontend dapp. // i.e. the frontend saved "hello: world" and we enhance it to "hello: world checked" data.hello = format!("{} checked", data.hello); // We encode the data back to blob. let encode_data = encode_doc_data(&data)?; // 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, version: context.data.data.after.version, }; // We save the document 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(())}// Other hooksinclude_satellite!(); ``` As outlined in the ([Quickstart](#quickstart)) chapter, run `juno dev build` to compile and deploy the code locally. When testing this feature, if you wait a bit before calling the getter, unlike in the previous step, you should now receive the modified "hello: world checked" text set by the hook. This delay occurs because serverless Functions run fully asynchronously from the request-response between your frontend and the Satellite. --- ## Assertions Assertions allow you to validate or reject operations before they are executed. They're useful for enforcing data integrity, security policies, or business rules inside your Satellite, and they run synchronously during the request lifecycle. ``` use junobuild_macros::assert_set_doc;use junobuild_satellite::AssertSetDocContext;use junobuild_utils::decode_doc_data;use serde::{Deserialize, Serialize};#[derive(Serialize, Deserialize)]struct NoteData {text: String,}#[assert_set_doc(collections = ["notes"])]fn assert_set_doc(context: AssertSetDocContext) -> Result<(), String> {let data: NoteData = decode_doc_data(&context.data.data.proposed.data)?; if data.text.to_lowercase().contains("hello") { return Err("The text must not include the word 'hello'".to_string()); } Ok(())} ``` This example ensures that any document added to the `notes` collection does not contain the word `"hello"` (case-insensitive). If it does, the operation is rejected before the data is saved. --- ## Calling Canisters on ICP You can make calls to other canisters on the Internet Computer directly from your serverless functions using `ic_cdk::call`. This is useful if you want to: * Fetch or modify data in other smart contracts * Interact with standard canisters like ledger or governance * Trigger behavior on other dapps Here's an example that calls another canister’s method and logs the result: ``` use candid::{CandidType, Principal, Nat};use ic_cdk::api::call;use ic_cdk::print;use junobuild_macros::on_set_doc;use junobuild_satellite::{OnSetDocContext};#[derive(CandidType)]struct SubAccount(Vec);#[derive(CandidType)]struct Account { owner: Principal, subaccount: Option,}type Icrc1Tokens = Nat;#[on_set_doc(collections = ["demo"])]async fn on_set_doc(context: OnSetDocContext) -> Result<(), String> { let account = Account { owner: context.caller, subaccount: None, }; let icp_ledger_id = Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").unwrap(); let (balance,): (Nat,) = call(icp_ledger_id, "icrc1_balance_of", (account,)) .await .map_err(|e| format!("Failed to call ICRC ledger icrc_balance_of: {:?}", e)) .map_err(|e| format!("Failed to get the balance: {:?}", e))?; print(format!("Balance: {}", balance)); Ok(())} ``` This example performs a call to the ICP Ledger canister using the `icrc1_balance_of` method to retrieve the token balance for the calling user. The response is printed to the log with `ic_cdk::print`. Note the use of tuple unpacking with `(balance,)`, which is required because `ic_cdk::call` always returns a tuple, even for single values. To encode and decode the call, you need Rust structures that match the Candid types used by the target canister. Candid is the format canisters use to talk to each other. You’ll usually find these types in the canister’s source code, its `.did` file, or you can generate them using tools like `didc` or `candid-extractor`. Feel free to reach out if you have questions. --- ## HTTPS outcalls [HTTPS outcalls](https://internetcomputer.org/https-outcalls) are a feature of the Internet Computer, enabling smart contracts to directly connect to the Web 2.0 world by querying APIs with HTTP requests. **Tip:** This example is also available on [GitHub](https://github.com/junobuild/examples/tree/main/rust/https-outcalls). For this example, we'll skip a few steps as the logic remains consistent: * Your frontend makes a call to the Satellite. * The Satellite performs some work, such as asserting and setting a document. * If everything succeeds, the Satellite triggers a hook before returning the result of the call. Here is an example of an `on_set_doc` hook which fetches an API to get the link to an image of a dog and saves that information within the Datastore. While this might not be a practical real-world use case, it is simple enough to demonstrate the feature. ``` use ic_cdk::api::management_canister::http_request::{ http_request as http_request_outcall, CanisterHttpRequestArgument, HttpMethod,};use junobuild_macros::{ on_delete_asset, on_delete_doc, on_delete_many_assets, on_delete_many_docs, on_set_doc, on_set_many_docs, on_upload_asset,};use junobuild_satellite::{ include_satellite, set_doc_store, OnDeleteAssetContext, OnDeleteDocContext, OnDeleteManyAssetsContext, OnDeleteManyDocsContext, OnSetDocContext, OnSetManyDocsContext, OnUploadAssetContext, SetDoc,};use junobuild_utils::encode_doc_data;use serde::{Deserialize, Serialize};// The data of the document we are looking to update in the Satellite's Datastore.#[derive(Serialize, Deserialize)]struct DogData { src: Option,}// 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, version: context.data.data.after.version, }; // 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) } }}// Other hooksinclude_satellite!(); ``` As with the previous example, the hook will asynchronously update the document. If you wait a bit before retrieving the document in your frontend, you might notice that the source of the image has been updated by your hook. ### Costs HTTPS outcalls require cycles to execute the request. At the time of writing this example, the cost was calculated using the formula `(3_000_000 + 60_000 * n) * n` for the base fee and `400 * n` each request byte and `800 * n` for each response byte, where n is the number of nodes in the subnet. You can use the [HTTPS Outcalls Cost Calculator](https://7joko-hiaaa-aaaal-ajz7a-cai.icp0.io/) to estimate the cost of your request (source code is available on [GitHub](https://github.com/domwoe/HTTPS-Outcalls-Calculator)). Alternatively, refer to the [documentation](https://internetcomputer.org/docs/current/references/https-outcalls-how-it-works#pricing) for the actual calculation method and costs. ### Technical Requirements The goal of HTTPS outcalls is to ensure that a request to the Web2 world returns a valid and verifiable response. To achieve this, calls are replicated when executed on mainnet. This means the blockchain will perform multiple identical requests and compare their results. The response will only succeed if all returned results are exactly the same. Many Web APIs do not natively support such replicated calls. More advanced APIs offer a way to handle this by using an **idempotency key**, a unique key that allows the server to recognize and return the same response for repeated requests. Another requirement is that the API must be accessible over **IPv6**. If replication or IPv6 support is not available, a common workaround is to use a proxy service. Developers can consider the following solutions: * [`ic-http-proxy`](https://github.com/omnia-network/ic-http-proxy) from Omnia Network ([announcement](https://forum.dfinity.org/t/non-replicated-https-outcalls/26627)) * [`idempotent-proxy`](https://github.com/ldclabs/idempotent-proxy) from LDC Labs ([announcement](https://forum.dfinity.org/t/idempotent-proxy-show-proxy-https-outcalls-to-any-web2-service/32754)) * A similar approach to the optimistic solution we currently used for transmitting emails ([repo](https://github.com/junobuild/proxy)) --- ## More Examples Here are additional examples showcasing different use cases for blockchainless in Juno. Each example is hosted on GitHub and provides a working implementation you can explore and modify. ### Essential Hooks A minimal app demonstrating how serverless function hooks work in Juno. This example provides a frontend interface for setting a document in the datastore or uploading a file to storage. The associated hooks perform basic operations: * Printing a message when triggered. * Demonstrating how to deserialize and serialize a document before updating and persisting it. This example serves as a great starting point for understanding how to interact with Juno's event-driven hooks in both datastore and storage contexts. [🔗 GitHub Repository](https://github.com/junobuild/examples/tree/main/rust/hooks) ### Generating JSON Files An example that demonstrates how to generate a JSON file within a serverless function on Juno. When a document is set, the corresponding hook creates a JSON file based on the document's data and automatically uploads it to storage. This makes the file accessible via an HTTPS URL, allowing it to be retrieved and used directly on the web. [🔗 GitHub Repository](https://github.com/junobuild/examples/tree/main/rust/json) # Use Juno with SvelteKit Explore how to create a Juno project developed with SvelteKit. What would you like to do? ([Build](#build))([Hosting](#hosting)) **Options:** Choose **Build** if you want to build a full featured rich application. Choose **Hosting** if you just want to deploy a website. --- ## Build Ready to implement a feature-rich application with Juno? You can choose a step-by-step approach, building each component gradually, or dive into our quickstart template, which showcases Juno's core features. Which path would you like to explore next? ([Step-by-step](#step-by-step))([Quickstart](#quickstart)) --- ### Step-by-step This guide provides quickstart instructions for integrating Juno in two scenarios: starting a new project and adding Juno to an existing SvelteKit app. #### 1\. Choose Your Integration Path You can either start a new project or add Juno to an existing app. ##### Path A: Start a new project with a template Create a new project using the Juno quickstart CLI: * npm * yarn * pnpm ``` npm create juno@latest -- --template sveltekit-starter ``` ``` yarn create juno -- --template sveltekit-starter ``` ``` pnpm create juno -- --template sveltekit-starter ``` ##### Path B: Integrate Juno into an existing SvelteKit app Navigate to your existing app: ``` cd your-existing-app ``` and install Juno SDK: * npm * yarn * pnpm ``` npm i @junobuild/core ``` ``` yarn add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` ``` pnpm add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` #### 2\. Start the Emulator If the Juno admin CLI (required to run the emulator) is not installed yet, run: ``` npm i -g @junobuild/cli ``` Once installed, start the local emulator: ``` juno dev start ``` Open the Console UI at [http://localhost:5866/](http://localhost:5866/). **Note:** When developing locally, you get an all-in-one emulator that closely mimics the production environment. This includes providing Juno and its Console UI locally. Sign in, create a Satellite, navigate to the **Datastore** section, and create a collection named **demo**. #### 3\. Configure To initialize the library with the Satellite ID you created, configure it in the `juno.config.ts` file (or other extension), which should be available at the root of your project. Replace `` with the ID. ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { ids: { development: "", production: "" }, source: "build", predeploy: ["npm run build"] }}); ``` #### 4\. Install the Plugin (If Needed) If you didn't start your project from a template, you'll need to install the plugin to automatically inject the Satellite ID into your app as an environment variable. Proceed as documented [here](/docs/reference/plugins.md#vite-plugin). #### 5\. Insert data from your app Create a new file `+layout.svelte` in `src/routes` and initialize your Satellite for your app. +layout.svelte ``` ``` Replace the existing content in your `+page.svelte` file in the same `routes` directory with the following code. +page.svelte ``` {#if doc !== undefined}Key: {doc.key}{/if} ``` #### 6\. Start the app Start the app, go to [http://localhost:5173](http://localhost:5173) in a browser, click "Insert a document," and you should see the data successfully persisted in your satellite on the blockchain. **What's Next: Going Live:** Once you're ready to deploy your app for others to access, continue to the ([Deployment guide](#deployment)). --- ### Quickstart This example demonstrates how to quickly deploy a basic note-taking app that integrates Juno's core features: * [Authentication](/docs/build/authentication.md): easy-to-use SDKs that support truly anonymous authentication. * [Datastore](/docs/build/datastore.md): a simple key-pair database for storing user data and other information. * [Storage](/docs/build/storage.md): a file storage system to store and serve user-generated content, such as photos. Using the Juno CLI, you can easily scaffold this app. * npm * yarn * pnpm ``` npm create juno@latest -- --template sveltekit-example ``` ``` yarn create juno -- --template sveltekit-example ``` ``` pnpm create juno -- --template sveltekit-example ``` Follow the CLI prompts to choose the note-taking app example and select local development. The CLI will manage all configurations and dependencies, allowing you to focus on exploring and customizing your app right away. --- ## Hosting If you already have an [SvelteKit](https://svelte.dev/) app, you're all set — proceed to the ([Deployment](#deployment)) section to upload your project to production. Otherwise, you can bootstrap a new website using the Juno template by running the following command: * npm * yarn * pnpm ``` npm create juno@latest -- --template sveltekit-starter ``` ``` yarn create juno -- --template sveltekit-starter ``` ``` pnpm create juno -- --template sveltekit-starter ``` Once you’re set up, continue to the ([Deployment](#deployment)) section below. --- ## Deployment Use this guide to deploy your project to production — directly to a smart contract on mainnet. ### 1\. Static site generation The Internet Computer, including Juno, currently does not support Server Side Rendering (_without workaround_). Therefore, it is recommended to generate a pre-rendered or client-side-only frontend application. We suggest using the [adapter-static](https://kit.svelte.dev/docs/adapter-static) option from SvelteKit and replacing the default adapter. Remove and install the adapter: ``` npm rm @sveltejs/adapter-auto && npm i -D @sveltejs/adapter-static ``` Update the import in `svelte.config.js` file: svelte.config.js ``` import adapter from "@sveltejs/adapter-static";/** @type {import('@sveltejs/kit').Config} */const config = { kit: { adapter: adapter() }};export default config; ``` Create a file `+layout.js` in `src/routes` to set the prerender option: +layout.js ``` export const prerender = true; ``` ### 2. Set up a satellite If you haven't created a satellite yet, go ahead and [create](/docs/create-a-satellite.md) a new one in the Juno's console. ### 3. Deploy Once your satellite is up and running, you can proceed with uploading your app to your smart contract. You can either automate your deployment with GitHub Actions (recommended) or deploy manually from your device. Choose your method: [GitHub Actions](/docs/guides/github-actions.md)[Manual](/docs/guides/manual-deployment.md) # Code Functions in TypeScript Learn how to develop, integrate, and extend Juno Satellites with serverless functions written in TypeScript. --- ## Quickstart Set up your environment to develop and extend a Satellite with custom serverless functions. First, ensure you have the Juno CLI installed. If you haven't installed it yet, run: * npm * yarn * pnpm ``` npm i -g @junobuild/cli ``` ``` yarn global add @junobuild/cli ``` ``` pnpm add -g @junobuild/cli ``` At the root of your application, eject the Satellite if you haven't already used a template. ``` juno dev eject ``` In a new terminal window, kick off the emulator: ``` juno dev start --watch ``` Now, your local development environment is up and running, ready for you to start coding. Every time you make changes to your code, it will automatically recompile and reload. --- ## Hooks and Data Operations Serverless Functions are triggered by hooks, which respond to events occurring in the Satellite, such as setting a document. Before implementing a hook that manipulates data ("backend"), let's first set up a JavaScript function in your ("frontend") dApp. Define a setter function in your frontend dApp as follows: ``` interface Example { hello: string;}let key: string | undefined;const set = async () => { key = crypto.randomUUID(); const record = await setDoc({ collection: "demo", doc: { key, data: { hello: "world" } } }); console.log("Set done", record);}; ``` This code generates a key and persists a document in a collection of the Datastore named "demo". Additionally, add a getter to your code: ``` const get = async () => { if (key === undefined) { return; } const record = await getDoc({ collection: "demo", key }); console.log("Get done", record);}; ``` Without a hook, executing these two operations one after the other would result in a record containing "hello: world". Now, let's create a hook within `src/satellite/index.ts` with the following implementation: ``` import { defineHook, type OnSetDoc } from "@junobuild/functions";import { decodeDocData, encodeDocData, setDocStore} from "@junobuild/functions/sdk";// The data shape stored in the Satellite's Datastoreinterface Person { hello: string;}// We declare a hook that listens to changes in the "demo" collectionexport const onSetDoc = defineHook({ collections: ["demo"], run: async (context) => { // Decode the document's data (stored as a blob) const data = decodeDocData(context.data.data.after.data); // Update the document's data by enhancing the "hello" field const updated = { hello: `${data.hello} checked` }; // Encode the data back to blob format const encoded = encodeDocData(updated); // Save the updated document using the same caller, collection, and key await setDocStore({ caller: context.caller, collection: context.data.collection, key: context.data.key, doc: { data: encoded, description: context.data.data.after.description, version: context.data.data.after.version } }); }}); ``` Once saved, your code should be automatically compiled and deployed. When testing this feature, if you wait a bit before calling the getter, you should now receive the modified "hello: world checked" text set by the hook. This delay occurs because serverless Functions execute fully asynchronously, separate from the request-response cycle between your frontend and the Satellite. --- ## Assertions Assertions allow you to validate or reject operations before they are executed. They're useful for enforcing data integrity, security policies, or business rules inside your Satellite, and they run synchronously during the request lifecycle. ``` import { decodeDocData } from "@junobuild/functions/sdk";import { defineAssert, type AssertSetDoc } from "@junobuild/functions";interface NoteData { text: string;}export const assertSetDoc = defineAssert({ collections: ["notes"], assert: (context) => { const data = decodeDocData(context.data.data.proposed.data); if (data.text.toLowerCase().includes("hello")) { throw new Error("The text must not include the word 'hello'"); } }}); ``` This example ensures that any document added to the `notes` collection does not contain the word `"hello"` (case-insensitive). If it does, the operation is rejected before the data is saved. --- ### Validating with Zod To simplify and strengthen your assertions, we recommend using [Zod](https://zod.dev/) — a TypeScript-first schema validation library. It's already bundled as a dependency of the `@junobuild/functions` package, so there's nothing else to install. Here's how you can rewrite your assertion using Zod for a cleaner and more declarative approach: ``` import { z } from "zod";import { decodeDocData } from "@junobuild/functions/sdk";import { defineAssert, type AssertSetDoc } from "@junobuild/functions";interface NoteData { text: string;}const noteSchema = z.object({ text: z .string() .refine( (value) => !value.toLowerCase().includes("hello"), "The text must not include the word 'hello'" )});export const assertSetDoc = defineAssert({ collections: ["notes"], assert: (context) => { const data = decodeDocData(context.data.data.proposed.data); noteSchema.parse(data); }}); ``` This approach is more expressive, easier to extend, and automatically gives you type safety and error messaging. If the validation fails, `parse()` will throw and reject the request. --- ## Calling Canisters on ICP This is useful if you want to: * Fetch or modify data in other smart contracts * Interact with standard canisters like ledger or governance * Trigger behavior on other dapps Here's an example that calls another canister’s method and logs the result: ``` import { call } from "@junobuild/functions/ic-cdk";import { defineHook, type OnSetDoc } from "@junobuild/functions";import { IDL } from "@dfinity/candid";import { Principal } from "@dfinity/principal";// Define Candid typesconst SubAccount = IDL.Vec(IDL.Nat8);const Account = IDL.Record({ owner: IDL.Principal, subaccount: IDL.Opt(SubAccount)});const Icrc1Tokens = IDL.Nat;// Define TypeScript interfacesexport type SubAccountType = Uint8Array | number[];export interface AccountType { owner: Principal; subaccount: [] | [SubAccountType];}export type Icrc1TokensType = bigint;// Define the onSetDoc hookexport const onSetDoc = defineHook({ collections: ["notes"], run: async (context) => { const account: AccountType = { owner: Principal.from(context.caller), subaccount: [] }; const icpLedgerId = Principal.fromText("ryjl3-tyaaa-aaaaa-aaaba-cai"); const balance = await call({ canisterId: icpLedgerId, method: "icrc1_balance_of", args: [[Account, account]], result: Icrc1Tokens }); console.log("Balance:", balance); }}); ``` This example performs a call to the ICP Ledger canister using the `icrc1_balance_of` method to retrieve the token balance for the calling user. The result is printed to the log using `console.log`. The `args` field contains a tuple with the Candid type definition and the corresponding JavaScript value. The `call` function handles both encoding the request and decoding the response using the provided types. To encode and decode these calls, you need JavaScript structures that match the Candid types used by the target canister. Currently, the best (and slightly annoying) way to get them is to copy/paste from the `service` output generated by tools like `didc`. It's not ideal, but that’s the current status. We’ll improve this in the future — meanwhile, feel free to reach out if you need help finding or shaping the types. --- ## Handling Multiple Collections If your hook applies to many collections, a switch statement is one way to route logic: ``` import { defineHook, type OnSetDoc } from "@junobuild/functions";export const onSetDoc = defineHook({ collections: ["posts", "comments"], run: async (context) => { switch (context.data.collection) { case "posts": // Handle posts logic break; case "comments": // Handle comments logic break; } }}); ``` While this works, you might accidentally forget to handle one of the observed collections. To prevent that, you can use a typed map: ``` import { defineHook, type OnSetDoc, type OnSetDocContext, type RunFunction} from "@junobuild/functions";const collections = ["posts", "comments"] as const;type OnSetDocCollection = (typeof collections)[number];export const onSetDoc = defineHook({ collections, run: async (context) => { const fn: Record> = { posts: yourFunction, comments: yourOtherFunction }; await fn[context.data.collection as OnSetDocCollection]?.(context); }}); ``` This ensures all collections are handled and you'll get a TypeScript error if one is missing. # Use Juno with Vue Explore how to create a Juno project developed with Vue. What would you like to do? ([Build](#build))([Hosting](#hosting)) **Options:** Choose **Build** if you want to build a full featured rich application. Choose **Hosting** if you just want to deploy a website. --- ## Build Ready to implement a feature-rich application with Juno? You can choose a step-by-step approach, building each component gradually, or dive into our quickstart template, which showcases Juno's core features. Which path would you like to explore next? ([Step-by-step](#step-by-step))([Quickstart](#quickstart)) --- ### Step-by-step This guide provides quickstart instructions for integrating Juno in two scenarios: starting a new project and adding Juno to an existing Vue app. #### 1\. Choose Your Integration Path You can either start a new project or add Juno to an existing app. ##### Path A: Start a new project with a template Create a new project using the Juno quickstart CLI: * npm * yarn * pnpm ``` npm create juno@latest -- --template vue-starter ``` ``` yarn create juno -- --template vue-starter ``` ``` pnpm create juno -- --template vue-starter ``` ##### Path B: Integrate Juno into an existing Vue app Navigate to your existing app: ``` cd your-existing-app ``` and install Juno SDK: * npm * yarn * pnpm ``` npm i @junobuild/core ``` ``` yarn add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` ``` pnpm add @junobuild/core @dfinity/agent @dfinity/auth-client @dfinity/candid @dfinity/identity @dfinity/principal ``` #### 2\. Start the Emulator If the Juno admin CLI (required to run the emulator) is not installed yet, run: ``` npm i -g @junobuild/cli ``` Once installed, start the local emulator: ``` juno dev start ``` Open the Console UI at [http://localhost:5866/](http://localhost:5866/). **Note:** When developing locally, you get an all-in-one emulator that closely mimics the production environment. This includes providing Juno and its Console UI locally. Sign in, create a Satellite, navigate to the **Datastore** section, and create a collection named **demo**. #### 3\. Configure To initialize the library with the Satellite ID you created, configure it in the `juno.config.ts` file (or other extension), which should be available at the root of your project. Replace `` with the ID. ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { ids: { development: "", production: "" }, source: "dist", predeploy: ["npm run build"] }}); ``` #### 4\. Install the Plugin (If Needed) If you didn't start your project from a template, you'll need to install the plugin to automatically inject the Satellite ID into your app as an environment variable. Proceed as documented [here](/docs/reference/plugins.md#vite-plugin). #### 5\. Insert data from your app In `App.vue`, or at the top of your app, initialize the Satellite when your app is mounted. Add an `insert` function to persist a document. App.vue ``` ``` #### 6\. Start the app Start the app, go to [http://localhost:5173](http://localhost:5173) in a browser, click "Insert a document," and you should see the data successfully persisted in your satellite on the blockchain. **What's Next: Going Live:** Once you're ready to deploy your app for others to access, continue to the ([Deployment guide](#deployment)). --- ### Quickstart This example demonstrates how to quickly deploy a basic note-taking app that integrates Juno's core features: * [Authentication](/docs/build/authentication.md): easy-to-use SDKs that support truly anonymous authentication. * [Datastore](/docs/build/datastore.md): a simple key-pair database for storing user data and other information. * [Storage](/docs/build/storage.md): a file storage system to store and serve user-generated content, such as photos. Using the Juno CLI, you can easily scaffold this app. * npm * yarn * pnpm ``` npm create juno@latest -- --template vue-example ``` ``` yarn create juno -- --template vue-example ``` ``` pnpm create juno -- --template vue-example ``` Follow the CLI prompts to choose the note-taking app example and select local development. The CLI will manage all configurations and dependencies, allowing you to focus on exploring and customizing your app right away. --- ## Hosting If you already have an [Vue](https://vuejs.org/) app, you're all set — proceed to the ([Deployment](#deployment)) section to upload your project to production. Otherwise, you can bootstrap a new website using the Juno template by running the following command: * npm * yarn * pnpm ``` npm create juno@latest -- --template vue-starter ``` ``` yarn create juno -- --template vue-starter ``` ``` pnpm create juno -- --template vue-starter ``` Once you're set up, continue to the ([Deployment](#deployment)) section below. --- ## Deployment Use this guide to deploy your project to production — directly to a smart contract on mainnet. ### 1. Set up a satellite If you haven't created a satellite yet, go ahead and [create](/docs/create-a-satellite.md) a new one in the Juno's console. ### 2. Deploy Once your satellite is up and running, you can proceed with uploading your app to your smart contract. You can either automate your deployment with GitHub Actions (recommended) or deploy manually from your device. Choose your method: [GitHub Actions](/docs/guides/github-actions.md)[Manual](/docs/guides/manual-deployment.md) # Deploy Frontend This section describes how to deploy the frontend of your project using GitHub Actions. The frontend typically includes all client-side assets — such as HTML, CSS, JavaScript, and other static files—that are served to users. With this setup, changes pushed to your repository can be automatically deployed based on your workflow configuration. --- ## Configuration To configure an action to deploy your frontend assets, follow these steps: 1. Create or edit a `deploy.yml` file in the `.github/workflows` subfolder of your repository. If the folders do not exist, create those. 2. Paste the following code into the file: deploy.yml ``` name: Deploy Frontend to Junoon: workflow_dispatch: push: branches: [main]jobs: deploy: 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: Deploy to Juno uses: junobuild/juno-action@main with: args: deploy env: JUNO_TOKEN: ${{ secrets.JUNO_TOKEN }} ``` Whenever code is pushed to your `main` branch, this action performs the following tasks: it checks out your repository, installs dependencies. It then utilizes the [junobuild/juno-action](https://github.com/junobuild/juno-action) GitHub Action to build and deploy your dapp. That's it—your pipeline is set! 🥳 **Note:** If your `juno.config` file does not build your application using a `predeploy` field, you might need to add an additional step to your YAML file to do so: ``` - name: Build run: npm run build ``` --- ## Optimization & Best Practices Below are key considerations to ensure efficient and cost-effective deployment of your project. ### Build Reproducibility Only new resources will be deployed to your satellite. Changes are detected through sha256 comparison. Therefore, ensuring the build reproducibility of your application is crucial to accurately identify and deploy the necessary updates. ### Deployment Costs Deploying new assets consumes \[cycles\], and the cost increases with both the frequency of deployments and the number of items to deploy. While the above code snippet demonstrates a more frequent lifecycle, as a general recommendation, consider minimizing your deployment expenses with less frequent deployments. For instance, you can trigger the action on releases instead. ``` on: release: types: [released] ``` # Build and Publish Serverless Functions This section explains how to automate the build and publication of your serverless functions using GitHub Actions. The process works for functions written in TypeScript or Rust and helps integrate function deployment into your development workflow. --- ## Configuration To configure an action to build and publish serverless functions, follow these steps: 1. Create or edit `publish.yml` in `.github/workflows/`. 2. Paste the following code into the file: publish.yml ``` name: Publish Serverless Functionson: 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 }} ``` This action will build and publish your serverless function bundle. If your access key has a **write** role, the changes will be automatically deployed to your Satellite's CDN. If your key only has a **submit** role, the release will be submitted as a pending change for manual approval. To avoid errors in submit-only workflows, you can explicitly use the `--no-apply` flag to skip auto-application. ``` - name: Publish uses: junobuild/juno-action@full with: args: functions publish --no-apply env: JUNO_TOKEN: ${{ secrets.JUNO_TOKEN }} ``` --- ## Optimization & Best Practices Below are key considerations to ensure efficient and cost-effective publication of your functions. ### Triggering on Release You can adjust the trigger to publish your serverless function only on releases, which helps reduce unnecessary CI runs and deployments. ``` on: release: types: [released] ``` This ensures that your function bundle is built and published only when a GitHub release is published. # Upgrade Serverless Functions (Optional) **Caution:** We do not recommend upgrading your container directly from CI in **production**. This approach hands over control to automation, which may not be suitable for critical environments. Prefer a change workflow and executing the upgrade with your CLI or in the Console UI. --- ## Configuration To configure an action to upgrade your Satellite container, follow these steps: 1. Create an `upgrade.yml` file in the `.github/workflows` subfolder of your repository. 2. Paste the following code into the file: upgrade.yml ``` name: Upgrade Satellite Containeron: workflow_dispatch:jobs: upgrade: 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: Upgrade uses: junobuild/juno-action@full with: args: functions upgrade env: JUNO_TOKEN: ${{ secrets.JUNO_TOKEN }} ``` # Monitoring Keeping your modules running smoothly is essential for any application. The monitoring feature ensures your [Mission Control](/docs/terminology.md#mission-control) (your wallet) and modules — Satellites and Orbiter (Analytics) — stay operational by automatically refilling cycles when they run low. This helps prevent unexpected downtime, allowing you to focus on building and growing your product without worrying about cycle balances. ![A screenshot of the monitoring overview within Juno Console](/assets/images/monitoring-dashboard-caf86757b1501c3fe41cbae3cffa3ff3.webp) --- ## Features * **Cycle refilling**: Monitored modules are automatically topped up when their balance falls below what's needed to stay active. * **Self-Monitoring**: Your Mission Control ensures that both your wallet and modules maintain sufficient cycles, with full control remaining in your hands at all times. * **Automatic ICP Conversion**: Can mint new cycles from the ICP in your wallet, ensuring your canisters stay adequately funded. * **Hourly Checks**: The system evaluates balances once an hour. --- ## Why Enable Monitoring? When a smart contract runs out of [cycles](/docs/terminology.md#cycles) on the [Internet Computer](https://internetcomputer.org), it stops functioning, which can disrupt your application or service. Enabling monitoring provides peace of mind by automating the management of cycles, ensuring your modules are always ready to perform. It also saves a little time by eliminating the need for manual top-ups. It's important to note that if your smart contracts — whether it's your wallet or a module — run out of cycles, they will enter a grace period. During this time, the module stops working but can still be restored. If no action is taken, the module eventually gets deleted, resulting in the permanent loss of its data and functionality. --- ## How does it work? Monitoring runs hourly within your Mission Control, which acts as the central hub for managing all monitored modules. The process follows these steps: --- ### Periodic Balance Checks Your Mission Control evaluates the cycle balances of all modules you've enabled for monitoring, including itself. Monitoring cannot be enabled without also observing Mission Control (your wallet), as it serves as the source for auto-refills. The system compares the cycle balance of each module (and Mission Control) against the sum of the grace period requirement and the trigger threshold you've configured. For example: * Grace Period Requirement: 0.5T Cycles * Trigger Required Amount: 0.5 T Cycles * \=> Total Required: 1.0T Cycles * Current Balance: 0.8 T Cycles Since the balance is 0.2 T Cycles below the required amount in this example, the system will attempt an auto-refill. --- ### Auto Refilling When a module is eligible for refill, Mission Control attempts to top it up by following these rules: #### a. Topping up from Mission Control's cycles If Mission Control has enough cycles above its own required amount, it uses them to refill the module. For example: * Mission Control Balance: 10 T Cycles * Mission Control Required Amount: 3 T Cycles * Module Required Amount: 1 T Cycles * Current Module Balance: 0.5 T Cycles * Top-Up Amount (Configured): 2 T Cycles In this case, Mission Control deducts 2 T Cycles to top up the module, leaving: * Mission Control Balance: 8 T Cycles * Module Balance: 2.5 T Cycles #### b. When Mission Control cannot top up If Mission Control's balance is below its own required amount, it cannot top up the module. For example: * Mission Control Balance: 10 T Cycles * Mission Control Required Amount: 12 T Cycles In this example, Mission Control's balance is already below its own requirement; therefore, no top-up can be performed unless it also holds ICP. #### c. Minting Cycles from ICP If Mission Control's balance is insufficient but it holds ICP, it can mint cycles to refill the module. For example: * Mission Control Balance: 10 T Cycles * Mission Control Required Amount: 12 T Cycles * ICP Balance: 1 ICP Mission Control uses part of its ICP to mint cycles: * Mission Control Balance (after minting): 10 T Cycles * Module Balance: 2.5 T Cycles * ICP Balance (after minting): ~0.75 ICP **Important:** Mission Control can only refill itself by minting new cycles with ICP. #### d. When no top-up is possible If Mission Control doesn't have enough cycles or ICP to perform a refill, the top-up fails. --- ### Notifications After each successful top-up, developers who have opted-in will receive an email notification. These notifications provide details such as the module that was refilled and the amount of cycles added. In the case of failed attempts (e.g., when Mission Control or a module could not be refilled), a single email notification is sent per day to avoid spamming developers in the event of repeated failures. --- ## Configuration To enable monitoring, go to the [Monitoring Section](https://console.juno.build/monitoring) in the Juno Console. Start the wizard by clicking **Enable Auto-Refill** and follow the prompts to select the modules you want to monitor, choose a pre-defined strategy or create a custom one, and enable the feature. The wizard simplifies the setup process, making it easy to configure monitoring according to your needs. # Snapshots Snapshots are an essential component of any disaster recovery strategy. In the fast-paced world of development, unexpected issues can arise—such as accidental overwrites of critical data or bugs in updates. While these scenarios can't always be avoided, their impact can be minimized or mitigated. The Snapshot feature acts as your safety net, ensuring that your project can recover quickly and efficiently from potential disasters. Each snapshot is a snapshot of your module (Satellite, Mission Control, or Orbiter) at a specific point in time. Snapshots enable you to restore your canister to its previous state, protecting your work and averting potential crises. --- ## How It Works You can manually create, restore, and delete snapshots for each module as needed. To do so, navigate to a module in the [console](/docs/terminology.md#console) and locate the features within the "Setup" tab. Snapshots are also automatically created during the upgrade process, capturing a snapshot when upgrading to a new version. If preferred, you can opt out in the advanced options to skip creating a snapshot or prevent overwriting an existing one. However, given the sensitivity of such processes, we strongly recommend always having a snapshot available or at least ensuring a way to restore your data—for example, by being prepared to redeploy your application quickly. --- ## Limitations As determined by the Internet Computer (see the [specification](https://internetcomputer.org/docs/current/references/ic-interface-spec#ic-take_canister_snapshot)), only one snapshot per module is currently allowed. If this evolves in the future, Juno will be updated accordingly. Snapshots are stored on-chain, and the cost of a snapshot’s memory consumption is charged to the module itself. This means they follow the lifecycle of your smart contracts — if the module is deleted or runs out of cycles, its snapshot is also removed. This is why snapshots should not be mistaken for backups. Unlike true backups, they do not provide historical retention, off-chain storage, or protection against accidental loss of control. For long-term data protection, external backups — such as secure cold storage for sensitive data — are recommended. --- ## Frequency of Snapshots Snapshots are automated during code upgrades, with the option to opt out. Alternatively, snapshots can be managed manually. # Access Keys Access keys play a crucial role in granting permissions to Mission Controls, Satellites or Analytics within Juno. When you sign in to Juno's [Console](/docs/terminology.md#console) using [Internet Identity](https://internetcomputer.org/internet-identity), you — and no one else (including not Juno) — become the owner of your [mission control](/docs/terminology.md#mission-control). This information is then sent back to your browser, where you can manage your modules. ![Juno's console flow](/assets/images/console-d0903e4989f7c4db5f4e85567211d266.png) When you create a [satellite](/docs/terminology.md#satellite), you and your mission control become its owners. Per extension, you — and no one else (including not Juno) — own your satellite. **Note:** * What was previously referred to as _controllers_ in earlier versions of the documentation is now called _administrative access keys_. The concept remains the same — only the terminology has been updated for clarity and consistency. * One access key is identified by a [principal](/docs/terminology.md#principal). --- ## Roles Each access key is assigned a **role** that defines what it can do: | Role (Internal) | Display Name | Can Submit | Can Apply/Commit | Can Deploy Immediately | Can Upgrade Immediately | | --- | --- | --- | --- | --- | --- | | **Admin** | Administrator | ✅ | ✅ | ✅ | ✅ | | **Write** | Editor | ✅ | ✅ | ✅ | ❌ | | **Submit** | Submitter | ✅ | ❌ | ❌ | ❌ | An **administrator** can perform tasks such as configuring or deploying an app, topping up a mission control or satellite, creating a new collection in the [datastore](/docs/build/datastore.md) or [storage](/docs/build/storage.md), or configuring a custom domain in the [hosting](/docs/build/hosting.md). An **editor** can publish new serverless function versions to a Satellite’s CDN, deploy your frontend application, and read data from a collection. However, it cannot directly upgrade a Satellite or start/stop a module. A **submitter** can propose changes—such as publishing a new version of a serverless function or frontend app—but those changes must be manually reviewed and applied using the Console UI or CLI. --- ## Generating Access Keys You can generate additional access keys to allow other developers, services, or CI pipelines to interact with your modules. When doing so, you can assign a role based on the level of access required. Access keys can be generated either through the Console UI or using the CLI. **Note:** You can generate a limited number of administrator access keys for a single module, in line with the limitation set by the [Internet Computer](https://internetcomputer.org/docs/current/references/ic-interface-spec#ic-create_canister). To accomplish this, you have two main options. **Tip:** When creating a new Satellite, it’s very likely that you’ll want to generate access keys for local development or to enable automated deployments from CI. Check out the guides: * [GitHub Actions](/docs/guides/github-actions.md) * [Manual Deployments](/docs/guides/manual-deployment.md) --- ## Generate an Access Key with the Console UI You can generate and manage access keys through the Console: 1. Go to [http://console.juno.build](http://console.juno.build) and select your module (Satellite, Analytics or Mission Control) 2. Open the **Setup** tab 3. Scroll to the **Access Keys** section and click **Add an access key** 4. Choose **Generate a new access key** 5. Select the desired role (e.g., **Administrator**) 6. Click **Submit** You can also manually enter an access key instead of generating one, if you wish to reuse an existing one. --- ## Generate an Access Key with the CLI When using the CLI, you can either reuse an existing access key or generate a new one. --- ### Reuse an existing access key When setting up an additional Satellite, you might want to reuse an existing access key already configured on your local machine. To do this, simply run: ``` juno login ``` and follow the instructions. When you run the command, the CLI checks if an access key is already present on your machine. If found, it will give you the option to either reuse the existing key or generate a new one. If you choose to reuse it, the CLI will guide you through the process. --- ### Generate a new access key To **generate a new access key** and attach it to your desired Mission Controls and Satellites, you can run: ``` juno login ``` The CLI will guide you through the process. This method is useful if you want to generate a completely new key and apply it across all your modules. **Note:** This action will overwrite the previously saved key used to configure your local CLI environment. # Best Practices This page provides recommendations to improve your application when developing and deploying with Juno. ## Content Security Policy (CSP) By default, a Satellite disables embedding your app in an iframe and sets various security headers - notably `X-Content-Type-Options`, `Strict-Transport-Security` and `Referrer-Policy` - to enhance protection against common web vulnerabilities. However, Juno does not enforce a Content Security Policy (CSP) by default, as doing so could make the developer experience — especially for beginners — challenging. That being said, we strongly recommend defining a CSP in your project for security reasons. A well-defined CSP helps prevent attacks such as cross-site scripting (XSS) and data injection. ### How to Set a CSP You can define your CSP in one of two ways: 1. **Static Definition** Add a CSP as the first `` tag of the `` in your HTML file. ``` ``` 2. **Via Configuration** Configure your Satellite to send a CSP header as part of the HTTP response. Headers can be configured as specified in this [chapter](/docs/reference/configuration.md#http-headers) of the documentation. ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", storage: { headers: [ { source: "**/*", headers: [["Content-Security-Policy", "REPLACE_THIS_WITH_YOUR_RULES"]] } ] } }}); ``` # Memory This page explains how memory works conceptually and how you can monitor its usage through the Console. --- ## General Memory Usage Every module — whether it's a [satellite](/docs/terminology.md#satellite), [orbiter](/docs/terminology.md#orbiter), or your [mission control](/docs/terminology.md#mission-control) — consumes memory in multiple ways. Some of that memory is directly controlled by your code, like the data you store in your [datastore](/docs/build/datastore.md) or [storage](/docs/build/storage.md). Other parts are more structural: global variables, the WASM binary (the container code itself), snapshots, and even system metadata contribute to your overall memory footprint. To help you understand and optimize memory usage, the Console displays a detailed breakdown under each module's overview tab. These metrics are especially helpful for staying within limits, controlling costs, and avoiding issues during upgrades. ### Metrics | Metric | Description | Keyword | | --- | --- | --- | | `wasm_memory_size` | Heap memory used by the module to save data. | Heap | | `stable_memory_size` | Stable memory used by the module to save data. | Stable | | `wasm_binary_size` | Size of the deployed container's code — specifically, the size of the installed gzipped WASM binary. | Code | | `wasm_chunk_store_size` | Memory used for storing chunks of the code when installing large containers (WASM > 2 MB). Used only in Mission Control. | Chunks | | `custom_sections_size` | Memory used to store metadata like version info or API definitions, stored in the container WASM binary's custom sections. | Metadata | | `canister_history_size` | Memory used by the module's history (e.g. creation, installation, upgrade, or controller changes). | History | | `snapshots_size` | Memory consumed by snapshots created for the module. | Snapshots | --- ## Satellite A [satellite](/docs/terminology.md#satellite) can store data using two types of memory: `heap` and `stable`. While both types are forms of random-access memory that only exist as long as the smart contract lives, they can be compared to a familiar analogy. Think of `heap` as similar to the RAM in a computer, and `stable` as more akin to ROM. ### In a nutshell `Heap` memory offers the best performance for accessing data, both for reading and writing. However, it has a limited capacity in terms of the space it can occupy, with a max of 1 GB. On the other hand, `stable` memory has a higher memory threshold with a maximum limit of 500 GB minus the heap size, allowing it to store more data in terms of size. However, it is slightly slower. Additionally, `heap` memory needs to be deserialized and serialized each time you upgrade the smart contract's code. This process becomes heavier as the heap memory size grows. On the contrary, `stable` memory doesn't require processing during an upgrade. However, the data it contains needs to be deserialized and serialized each time it is accessed, which can make its usage more costly. ### Recommendations There are no strict rules governing the choice of memory type for your use case. Ultimately, the decision lies with you, based on what best suits your project. This is why both the [datastore](/docs/build/datastore.md) and [storage](/docs/build/storage.md) support both memory types. In practice, `heap` memory can be recommended for small datasets or data that require quick or frequent access, while `stable` memory is preferred for large data or data accessed less often. This is why, for example, your dapp's bundle assets (including JS, HTML, images, etc.) are stored within the `heap` memory of satellites. However, this decision, along with the memory limitations, results in a significant portion of the `heap` memory being allocated. Although `stable` memory is slightly slower and comes at a higher cost, it is well-suited for storing data and ensuring smooth smart contract upgrades. This is particularly important for the operation and lifecycle of a project. That's why the default option for creating new collections is set to `stable` for both datastore and storage. ### Default usage As mentioned in the previous chapter, your dapp's bundle and assets (everything you deploy to your satellite using `juno deploy`), are stored in the `heap` memory. In contrast, your users (as of Satellite version 0.0.16) and the [analytics](/docs/build/analytics.md) data are saved within `stable` memory. ### Summary | Aspect | Heap Memory | Stable Memory | | --- | --- | --- | | **Capacity** | Max 1 GB | Max 500 GB (minus heap size) | | **Performance** | Fast for read and write operations | Slightly slower | | **Cost** | Lower cost | Higher cost (~20x) | | **Upgrades** | Data must be deserialized/serialized during upgrades | Data are not processed during upgrades | | **Usage** | Suitable for small or frequently accessed data | Suitable for large or less frequently accessed data | ## Resources * [Measure different collection libraries written in Rust](https://dfinity.github.io/canister-profiling/collections/) # Provisioning Options The creation wizard for Satellites and Orbiters includes advanced provisioning options for developers who need more control. --- ### Selecting a Subnet If you want more control over where your module is provisioned, you can select a [subnet](/docs/terminology.md#subnet) during the creation process. Below is a list of available subnets with relevant metadata to help you choose the most appropriate one: **Note:** Satellites and Orbiters running on larger subnets with more nodes will incur higher cycle costs compared to those on the standard 13-node subnets. However, this increased cost comes with the benefit of enhanced security due to the greater size of the subnet. For most applications, we recommend using the default subnets and staying on the same subnet as Juno. | Subnet ID | Type | Canisters (Running/Stopped) | Nodes (Up/Total) | | --- | --- | --- | --- | | 6pbhf-qzpdk-kuqbr-pklfa-5ehhf-jfjps-zsj6q-57nrl-kzhpd-mu7hc-vae | Juno's Subnet | 35216/689 | 13/13 | | pzp6e-ekpqk-3c5x7-2h6so-njoeq-mt45d-h3h6c-q3mxf-vpeq5-fk5o7-yae | Fiduciary | 3030/8 | 34/34 | | bkfrj-6k62g-dycql-7h53p-atvkj-zg4to-gaogh-netha-ptybj-ntsgw-rqe | European | 24912/593 | 13/13 | | brlsh-zidhj-3yy3e-6vqbz-7xnih-xeq2l-as5oc-g32c4-i5pdn-2wwof-oae | | 35034/728 | 13/13 | | o3ow2-2ipam-6fcjo-3j5vt-fzbge-2g7my-5fz2m-p4o2t-dwlc4-gt2q7-5ae | | 57088/62 | 12/13 | | 4ecnw-byqwz-dtgss-ua2mh-pfvs7-c3lct-gtf4e-hnu75-j7eek-iifqm-sqe | | 8063/120 | 13/13 | | opn46-zyspe-hhmyp-4zu6u-7sbrh-dok77-m7dch-im62f-vyimr-a3n2c-4ae | | 40021/721 | 13/13 | | nl6hn-ja4yw-wvmpy-3z2jx-ymc34-pisx3-3cp5z-3oj4a-qzzny-jbsv3-4qe | | 31491/713 | 13/13 | | io67a-2jmkw-zup3h-snbwi-g6a5n-rm5dn-b6png-lvdpl-nqnto-yih6l-gqe | | 942/372 | 13/13 | | ejbmu-grnam-gk6ol-6irwa-htwoj-7ihfl-goimw-hlnvh-abms4-47v2e-zqe | | 10454/9 | 13/13 | | gmq5v-hbozq-uui6y-o55wc-ihop3-562wb-3qspg-nnijg-npqp5-he3cj-3ae | | 34008/166 | 13/13 | | pjljw-kztyl-46ud4-ofrj6-nzkhm-3n4nt-wi3jt-ypmav-ijqkt-gjf66-uae | | 32360/158 | 13/13 | | 4zbus-z2bmt-ilreg-xakz4-6tyre-hsqj4-slb4g-zjwqo-snjcc-iqphi-3qe | | 55218/4 | 13/13 | | 5kdm2-62fc6-fwnja-hutkz-ycsnm-4z33i-woh43-4cenu-ev7mi-gii6t-4ae | | 12830/50 | 13/13 | | e66qm-3cydn-nkf4i-ml4rb-4ro6o-srm5s-x5hwq-hnprz-3meqp-s7vks-5qe | | 35390/677 | 13/13 | | qdvhd-os4o2-zzrdw-xrcv4-gljou-eztdp-bj326-e6jgr-tkhuc-ql6v2-yqe | | 53203/19 | 13/13 | | snjp4-xlbw4-mnbog-ddwy6-6ckfd-2w5a2-eipqo-7l436-pxqkh-l6fuv-vae | | 1326/815 | 13/13 | | shefu-t3kr5-t5q3w-mqmdq-jabyv-vyvtf-cyyey-3kmo4-toyln-emubw-4qe | | 664/598 | 13/13 | | csyj4-zmann-ys6ge-3kzi6-onexi-obayx-2fvak-zersm-euci4-6pslt-lae | | 1092/248 | 13/13 | | yinp6-35cfo-wgcd2-oc4ty-2kqpf-t4dul-rfk33-fsq3r-mfmua-m2ngh-jqe | | 7346/41 | 13/13 | | w4asl-4nmyj-qnr7c-6cqq4-tkwmt-o26di-iupkq-vx4kt-asbrx-jzuxh-4ae | | 504/1426 | 13/13 | | mpubz-g52jc-grhjo-5oze5-qcj74-sex34-omprz-ivnsm-qvvhr-rfzpv-vae | | 55420/244 | 13/13 | | fuqsr-in2lc-zbcjj-ydmcw-pzq7h-4xm2z-pto4i-dcyee-5z4rz-x63ji-nae | | 20669/20 | 13/13 | | cv73p-6v7zi-u67oy-7jc3h-qspsz-g5lrj-4fn7k-xrax3-thek2-sl46v-jae | | 51531/272 | 12/13 | | pae4o-o6dxf-xki7q-ezclx-znyd6-fnk6w-vkv5z-5lfwh-xym2i-otrrw-fqe | | 3729/23 | 13/13 | | qxesv-zoxpm-vc64m-zxguk-5sj74-35vrb-tbgwg-pcird-5gr26-62oxl-cae | | 401/952 | 13/13 | | lspz2-jx4pu-k3e7p-znm7j-q4yum-ork6e-6w4q6-pijwq-znehu-4jabe-kqe | | 39574/875 | 13/13 | | jtdsg-3h6gi-hs7o5-z2soi-43w3z-soyl3-ajnp3-ekni5-sw553-5kw67-nqe | | 27310/140 | 13/13 | # Wallet This section provides guidance on managing your assets and cycles with your [wallet](/docs/terminology.md#wallet), which are essential for maintaining and providing enough resources for your modules in the Juno ecosystem. **Important:** Just like your modules, your wallet is fully under your control — Juno cannot access, move, or recover the ICP or cycles held inside. Because of this non-custodial model, there are no refunds, reversals, or recovery options. Always double-check destination addresses before sending funds. As a best practice, we recommend not holding large amounts of ICP in your Juno wallet unless necessary. Use it as a utility wallet for fueling your modules — not as a long-term vault. We also recommend enabling [monitoring](/docs/management/monitoring.md) to ensure your Mission Control stays alive and responsive at all times. --- ## What are ICP? ICP are the native cryptocurrency of the [Internet Computer](https://internetcomputer.org). They are used to power this blockchain, providing utility and governance. One key usage is converting ICP tokens to cycles, which are used to cover the computational and storage costs of running smart contracts and modules. --- ## Why do I need ICP? Given that Juno is built on top of the Internet Computer (see [architecture](/docs/white-paper/architecture.md)), your smart contracts require cycles to remain active. While you don’t necessarily need ICP in the Juno ecosystem since you can acquire cycles with Stripe through [cycle.express](https://cycle.express), having some ICP can still be interesting. It provides independence by allowing you to top up your modules without relying on third-party services. Depending on how you obtain your tokens, using ICP can also help lower transaction costs and offers interoperability with other Internet Computer projects, making it a flexible and practical option. --- ## Buying ICP To get ICP from the outside world into your wallet, you can use most cryptocurrency exchange platforms that allow you to buy ICP (refer to this [list](https://coinranking.com/fr/coin/aMNLwaUbY+internetcomputerdfinity-icp/exchanges) of major ones). These platforms let you convert dollars (or other currencies) into ICP. Keep in mind that exchanges charge a fee for this service. Once you have obtained ICP on those platforms, you can initiate a transaction to send it to your wallet. For this purpose, you will need to provide a destination address where the ICP should be sent. This destination address corresponds to the [Account Identifier](/docs/terminology.md#account-identifier) of your wallet. You can locate the destination address in Juno's [console](https://console.juno.build). Once you've logged in, go to your [wallet](https://console.juno.build/wallet) and click "Receive". ![A screenshot of the wallet with the "Receive" button](/assets/images/wallet-receive-dbb873738cb48ce074e1201c3fff535f.png) Select "Account identifier". ![A screenshot of the wallet "Receive" modal with an arrow pointing to the Account Identifier option](/assets/images/wallet-receive-account-identifier-e320146e191bcf52d00d22238a92bb4b.png) Either copy your account identifier or use the provided QR code. This is the address you should use to receive ICP from the outside world. ![A screenshot of the Account Identifier and QR code](/assets/images/wallet-receive-account-identifier-qrcode-ea0f266be5c83547e4843d4a4b5a7a4b.png) --- ## Receiving ICP If you already hold ICP, you can transfer it from wallets within the ecosystem such as the [NNS dapp](https://nns.internetcomputer.org/), [OISY](https://oisy.com) or [others](https://internetcomputer.org/ecosystem?tag=Wallet). To initiate a transaction to send it to your wallet, you will need to provide a destination address, which in this case is your wallet ID. You can locate your wallet ID in Juno's [console](https://console.juno.build). Once you've logged in, go to your [wallet](https://console.juno.build/wallet), where the information is easy to find. ![A screenshot of the wallet with "Wallet ID" information](/assets/images/wallet-id-a806ae0c58411c9129c604868d98f7b0.png) If you wish to use a QR code, click "Receive" and select "Wallet ID". ![A screenshot of the wallet "Receive" modal with an arrow pointing to the Wallet ID option](/assets/images/wallet-receive-wallet-id-da09f22b7fe82d18b42836c013d451cd.png) Either copy your account identifier or use the provided QR code. This is the address you should use to transfer ICP within the ecosystem. ![A screenshot of the Wallet ID and QR code](/assets/images/wallet-receive-wallet-id-qrcode-de9cc6b8681da618c8758a364fb18d05.png) If you are using OISY, you can also connect this third-party wallet to Juno's console to initiate the transaction and proceed with the approval. This eliminates the need to copy, paste, or scan any addresses. ![A screenshot of the wallet "Receive" modal with an arrow pointing to the OISY option](/assets/images/wallet-receive-oisy-a6dabf8ca3d87e45da773c58f8a0eda3.png) --- ## Send ICP Sending ICP to the ecosystem or the outside world can be initiated from your wallet in Juno's [console](https://console.juno.build). To start a transaction, click "Send". ![A screenshot of the wallet with the "Send" button](/assets/images/wallet-send-09f0b1267286be0ebb67a985e432deb8.png) Enter the destination wallet ID or account identifier where you want to send ICP, along with the amount. ![A screenshot of the wallet send form](/assets/images/wallet-send-form-03440e3f0718c16c0b08a70cfa18d0ee.png) Review the transaction details and confirm to execute it. ![A screenshot of the wallet send review mask](/assets/images/wallet-send-review-52b726f5d9cc0977a04dba319dfa1ec3.png) # Workarounds This page is dedicated to helping you make the most of Juno features, even when some functionalities are not yet fully supported out of the box. Below, you'll find practical workarounds and guidance for processes which in the future will be resolved by features planned in the [roadmap](/docs/white-paper/roadmap.md). --- ## Transferring a Satellite to another "Account" Although Juno does not yet support direct satellite transfers - such as if you want to hand over a project to your friends or colleagues - you can use the following steps as a workaround: **Note:** There is no "account" on Juno. The Console solely holds a key-pair list of the developer IDs with their respective Mission Control IDs. All data and control are entirely managed by you. Nevertheless, for this tutorial, "account" refers to someone able to sign in into the Juno Console and who has a Mission Control. #### 1\. Add the new access key to the Satellite **Danger:** Never ever add access keys to your modules without being absolutely certain of their validity. Double-check everything before performing such a procedure. In your satellite, assign the access key of the destination account with administrative permissions: * The Mission Control ID * The Developer ID (available under "Preferences") In other words, ask your friend or colleague for their Developer ID and Mission Control ID, and add those as new access keys. Again, **please be absolutely certain** before adding the controllers. #### 2\. Attach Satellite in destination account The destination account — your friend or colleague — can use the "Attach Satellite" feature in their [Mission Control](https://console.juno.build/mission-control/). To do this, they will need the Satellite ID. By doing so, and because you have set them as a controller in the previous step, the Satellite you are transferring will be linked with their account and made available in the Console. **Tip:** At this stage, the satellite is linked to both accounts, making it accessible from each. If your goal is to share the satellite, you can consider this process complete and stop here. Otherwise, continue with the next steps. #### 3\. Remove Unnecessary Controllers The destination account — your friend or colleague, who is now the owner of the satellite — can then remove the access key(s) that should no longer be listed, specifically your own Developer ID and your Mission Control ID. #### 4\. Confirm and Detach Wait for confirmation that the destination account has completed all the steps and successfully taken over the satellite. Once confirmed, you can remove the satellite from your console using the "Detach Satellite" option available in the satellite overview. --- ## How to collaborate on the same "project" Since team collaboration isn't available yet, developers often find ways to share a satellite with friends or colleagues by either adding access keys—explained in the previous section—or by sharing an Internet Identity. With the latter approach, everyone in the group logs into the Juno Console using the same identity, allowing them to access the same data effortlessly. While this method falls outside Juno's intended scope, it works similarly to adding multiple passkeys to your own Internet Identity. Here’s a simple guide to help you set it up. **Important:** Sharing the same identity also means sharing access to the same wallet linked to that identity. Be sure to follow this approach only with people you trust. #### 1\. Create a new identity Go to [identity.internetcomputer.org](https://identity.internetcomputer.org) and create a new identity. #### 2\. Set up Sign into the [Juno Console](https://console.juno.build) using the new identity to create a new Mission Control and set up your first or subsequent satellites. #### 3\. Plan the identity sharing Arrange a call or meeting with the person you want to share the identity with, as the upcoming steps require actions to be completed within a short time frame. #### 4\. Sign into your Internet Identity Once ready, sign again into [identity.internetcomputer.org](https://identity.internetcomputer.org) using the newly created identity. #### 5\. Add a new passkey Click "Add new passkey" to initiate the process of adding the other person’s device. #### 6\. Share the generated link A link (e.g.,`https://identity.internetcomputer.org/?action=add-passkey&ii=1122333` for an identity number `1122333`) will be generated and valid for 15 minutes. Copy and send this link to the person you want to share the identity with. #### 7\. Approve the new passkey The recipient should open the link and approve the passkey by trusting their device. #### 8\. Verify with a code A six-digit code will be displayed on their screen, which they should provide to you. Enter this code on your screen to finalize the process. #### 9\. Confirm success Once the code is entered correctly, the new passkey will be added. Your collaborator can now sign into the Juno Console using the shared identity and access the same satellites. #### 10\. Optional: Rename passkeys It's recommended to rename the newly added passkey in Internet Identity to keep track of which passkey belongs to whom. **Note:** A maximum of 8 passkeys can be assigned to an identity, allowing you to share a project with up to 7 other people. # CLI The Juno CLI provides a variety of tools for managing and deploying projects. ## Installing the Juno CLI To download and install Juno CLI, run the following command: ``` npm i -g @junobuild/cli ``` --- ## Administration Commands dedicated to configuring and managing deployed Satellites, Mission Control, and other modules. **Tip:** The CLI automatically runs in non-interactive mode if either a `JUNO_TOKEN` is set or the `--headless` argument is used. The former is set when you set up a [GitHub Actions](/docs/guides/github-actions.md). --- ### Login **Important:** Authenticating your terminal saves sensitive information on your device. We recommend setting up a password to encrypt this file when prompted. Generate an authentication for use in non-interactive environments. ``` Usage: juno login [options]Options: -b, --browser A particular browser to open. supported: chrome|firefox|edge. -h, --help Output usage information. ``` **Note:** The authentication process requires a browser. If you've previously authenticated your terminal and decide to log in again, the CLI will prompt you about reusing your existing identity. This allows you to reuse your authorization, especially when creating new modules like satellites or orbiters. #### How does it work? A new [principal](/docs/terminology.md#principal) is generated on your local machine and added as a [controller](/docs/terminology.md#controller) of the selected modules. This principal is then used to authenticate any CLI calls made from your terminal to your satellites, mission controls or other modules. The key is saved in the OS-specific user's variables path. | OS | Path | | --- | --- | | Mac | `~/Library/Preferences/juno-nodejs` | | Windows | `%APPDATA%\juno-nodejs\Config` (for example, `C:\Users\USERNAME\AppData\Roaming\juno-nodejs\Config`) | | Linux | `~/.config/juno-nodejs` (or `$XDG_CONFIG_HOME/juno-nodejs`) | --- ### Logout **Caution:** This action currently does not remove the controllers from satellites and/or mission control and/or orbiter. It only logs out your local machine by removing the locally saved key (principal). Log out of the current device. ⚠️ This action does not remove the access keys from the module. ``` Usage: juno logout [options]Options: -h, --help Output usage information. ``` --- ### Init Set up your project. ``` Usage: juno init [options]Options: --minimal Skip few prompts and generate a config file with a placeholder satellite ID. -m, --mode Set env mode. For example production or a custom string. Default is production. --container-url Override a custom container URL. If not provided, defaults to production or the local container in development mode. --console-url Specify a custom URL to access the developer Console. -h, --help Output usage information. ``` The `juno init` command creates a `juno.config` file in the root directory of your project. Depending on your project, it will either create a TypeScript, JavaScript, or JSON file. **Tip:** We recommend using the first two options because they can leverage your IDE's IntelliSense with type hints. This file is necessary for deploying, configuring, or running any other CLI commands for your app. Read more about the [configuration](/docs/reference/configuration.md). --- ### Deploy Deploy your app to your satellite. ``` Usage: juno deploy [options]Options: -c, --clear Clear existing app files before proceeding with deployment. --no-apply Submit the deployment as a change but do not apply it yet. -k, --keep-staged Keep staged assets in memory after applying the change. -i, --immediate Deploy files instantly (bypasses the change workflow). -m, --mode Set env mode. For example production or a custom string. Default is production. --container-url Override a custom container URL. If not provided, defaults to production or the local container in development mode. --console-url Specify a custom URL to access the developer Console. -h, --help Output usage information.Notes:- The option --keep-staged only applies when --no-apply is NOT used (i.e. the change is applied immediately). ``` --- ### Config Apply configuration to satellite. ``` Usage: juno config [options]Options: -m, --mode Set env mode. For example production or a custom string. Default is production. --container-url Override a custom container URL. If not provided, defaults to production or the local container in development mode. --console-url Specify a custom URL to access the developer Console. -h, --help Output usage information. ``` --- ### Snapshot Handle snapshot-related tasks. ``` Usage: juno snapshot [options]Subcommands: create Create a snapshot of your current state. restore Restore a previously created snapshot. delete Delete an existing snapshot.Options: -t, --target Which module type should be snapshotted? Valid targets are satellite, mission-control or orbiter. -m, --mode Set env mode. For example production or a custom string. Default is production. --container-url Override a custom container URL. If not provided, defaults to production or the local container in development mode. --console-url Specify a custom URL to access the developer Console. -h, --help Output usage information.Notes:- Targets can be shortened to s for satellite, m for mission-control and o for orbiter. ``` --- ### Stop Stop a module. ``` Usage: juno stop [options]Options: -t, --target Which module type should be stopped? Valid targets are satellite, mission-control or orbiter. -m, --mode Set env mode. For example production or a custom string. Default is production. --container-url Override a custom container URL. If not provided, defaults to production or the local container in development mode. --console-url Specify a custom URL to access the developer Console. -h, --help Output usage information.Notes:- Targets can be shortened to s for satellite, m for mission-control and o for orbiter. ``` --- ### Start Start a module. ``` Usage: juno start [options]Options: -t, --target Which module type should be started? Valid targets are satellite, mission-control or orbiter. -m, --mode Set env mode. For example production or a custom string. Default is production. --container-url Override a custom container URL. If not provided, defaults to production or the local container in development mode. --console-url Specify a custom URL to access the developer Console. -h, --help Output usage information.Notes:- Targets can be shortened to s for satellite, m for mission-control and o for orbiter. ``` --- ### Clear Clear existing app code by removing JavaScript, HTML, CSS, and other files from your satellite. ``` Usage: juno clear [options]Options: -f, --fullPath Clear a particular file of your app. -m, --mode Set env mode. For example production or a custom string. Default is production. --container-url Override a custom container URL. If not provided, defaults to production or the local container in development mode. --console-url Specify a custom URL to access the developer Console. -h, --help Output usage information. ``` **Note:** This command removes existing files from the satellite and only affects the app assets. The user's uploaded files will not be cleared as the app is deployed to a reserved collection, `#dapp`. --- ### Upgrade Upgrade a module to a new version. ``` Usage: juno upgrade [options]Options: -t, --target Which module type should be upgraded? Valid targets are satellite, mission-control or orbiter. -s, --src A path to a specific local gzipped WASM file to publish. --clear-chunks Clear any previously uploaded WASM chunks (applies if the WASM size is greater than 2MB). --no-snapshot Skip creating a snapshot before upgrading. -r, --reset Reset to the initial state. -m, --mode Set env mode. For example production or a custom string. Default is production. --container-url Override a custom container URL. If not provided, defaults to production or the local container in development mode. --console-url Specify a custom URL to access the developer Console. -h, --help Output usage information.Notes:- Resetting a mission control is not possible.- Targets can be shortened to s for satellite, m for mission-control and o for orbiter. ``` **Important:** * We recommend that you stay current with the Juno releases, as some features may not perform correctly in the [console](/docs/terminology.md#console) if your smart contracts are outdated. * Upgrading requires a stable internet connection for a successful process. The CLI automatically runs in non-interactive mode if either a JUNO\_TOKEN is set or the --headless argument is used. --- ## Changes Review and apply changes submitted to your module. ``` Usage: juno changes [options]Subcommands: apply Apply a submitted change. list List all submitted or applied changes. reject Reject a change. ``` --- ### Apply Apply a submitted change. ``` Usage: juno changes apply [options]Options: -i, --id The ID of the change to apply. --snapshot Create a snapshot before applying. --hash The expected hash of all included changes (for verification). -k, --keep-staged Keep staged assets in memory after applying the change. -h, --help Output usage information. ``` --- ### List List all submitted or applied changes. ``` Usage: juno changes list [options]Options: -a, --all Search through all changes, not just the 100 most recent. -e, --every Include changes of any status (default is only submitted ones). -h, --help Output usage information. ``` --- ### Reject Reject a change. ``` Usage: juno changes reject [options]Options: -i, --id The ID of the change to reject. --hash The expected hash of all included changes (for verification). -k, --keep-staged Keep staged assets in memory after applying the change. -h, --help Output usage information. ``` --- ## Development Handle developer tasks like starting/stopping a local network. ``` Usage: juno dev [options]Subcommands: start Start a local Internet Computer network in a container. stop Stop the local network. build Alias for juno functions build. eject Alias for juno functions eject. ``` --- ### Start Start a local Internet Computer network in a container. ``` Usage: juno dev start [options]Options: -l, --lang Specify the language for building the serverless functions: rust, typescript or javascript. --cargo-path Path to the Rust manifest. --source-path Optional path to the TypeScript or JavaScript entry file. -w, --watch Rebuild your functions automatically when source files change. -h, --help Output usage information.Notes:- The language and path options are only used in combination with watch.- If no language is provided, the CLI attempts to determine the appropriate build.- Language can be shortened to rs for Rust, ts for TypeScript and mjs for JavaScript.- Use --cargo-path to specify a specific crate path. For Rust builds, this maps to --manifest-path for cargo build. For TypeScript and JavaScript, it points to the Rust crate (commonly "Sputnik") that imports the functions.- An optional --source-path to specify the source file for TypeScript and JavaScript (e.g. index.ts or index.mjs).- The watch option rebuilds when source files change, with a default debounce delay of 10 seconds; optionally, pass a delay in milliseconds. ``` --- ## Functions Build and upgrade your satellite's serverless functions. ``` Usage: juno functions [options]Subcommands: build Build your functions. eject Scaffold the necessary files for developing your serverless functions. publish Publish a new version of your functions. upgrade Upgrade your satellite's serverless functions.Notes:- The local server supports live reloading.- You can use fn as a shortcut for functions. ``` --- ### Build Build your serverless functions. ``` Usage: juno functions build [options]Options: -l, --lang Specify the language for building the serverless functions: rust, typescript or javascript. --cargo-path Path to the Rust manifest. --source-path Optional path to the TypeScript or JavaScript entry file. -w, --watch Rebuild your functions automatically when source files change. -h, --help Output usage information.Notes:- If no language is provided, the CLI attempts to determine the appropriate build.- Language can be shortened to rs for Rust, ts for TypeScript and mjs for JavaScript.- Use --cargo-path to specify a specific crate path. For Rust builds, this maps to --manifest-path for cargo build. For TypeScript and JavaScript, it points to the Rust crate (commonly "Sputnik") that imports the functions.- An optional --source-path to specify the source file for TypeScript and JavaScript (e.g. index.ts or index.mjs).- The watch option rebuilds when source files change, with a default debounce delay of 10 seconds; optionally, pass a delay in milliseconds. ``` --- ### Eject Generate the required files to begin developing serverless functions in your project. ``` Usage: juno functions eject [options]Options: -l, --lang Specify the language for building the serverless functions: rust, typescript or javascript. -h, --help Output usage information.Notes:- Language can be shortened to rs for Rust, ts for TypeScript and mjs for JavaScript. ``` --- ### Publish Publish a new version of your serverless functions. ``` Usage: juno functions publish [options]Options: --no-apply Submit the release as a change but do not apply it yet. -k, --keep-staged Keep staged assets in memory after applying the change. -s, --src A path to a specific local gzipped WASM file to publish. -m, --mode Set env mode. For example production or a custom string. Default is production. --container-url Override a custom container URL. If not provided, defaults to production or the local container in development mode. --console-url Specify a custom URL to access the developer Console. -h, --help Output usage information.Notes:- The option --keep-staged only applies when --no-apply is NOT used (i.e. the change is applied immediately). ``` --- ### Upgrade Upgrade your serverless functions. ``` Usage: juno functions upgrade [options]Options: --cdn Select a previously published WASM file from the CDN (interactive). --cdn-path Use a specific published WASM file from the CDN. --clear-chunks Clear any previously uploaded WASM chunks (applies if the WASM size is greater than 2MB). --no-snapshot Skip creating a snapshot before upgrading. -r, --reset Reset to the initial state. -s, --src A path to a specific local gzipped WASM file to publish. -h, --help Output usage information.Notes:- If no option is provided, the default local build output will be used.- If --src is specified, it takes precedence over any CDN options.- Use --cdn to interactively select from recent published releases. ``` --- ## Others Additional commands for managing and interacting with the CLI environment. --- ### Open Open your satellite in your browser. ``` Usage: juno open [options]Options: -b, --browser A particular browser to open. supported: chrome|firefox|edge. -c, --console Open satellite in the console. -m, --mode Set env mode. For example production or a custom string. Default is production. --container-url Override a custom container URL. If not provided, defaults to production or the local container in development mode. --console-url Specify a custom URL to access the developer Console. -h, --help Output usage information. ``` --- ### Use Switch between multiple profiles. ``` Usage: juno use [options]Options: -p, --profile The profile that should be use. -l, --list What are the available profiles. -h, --help Output usage information. ``` --- ### Version Check the version of the modules and CLI. ``` Usage: juno init [options]Options: -c, --cli Check only the version of the CLI. -m, --mode Set env mode. For example production or a custom string. Default is production. --container-url Override a custom container URL. If not provided, defaults to production or the local container in development mode. --console-url Specify a custom URL to access the developer Console. -h, --help Output usage information. ``` --- ### Who am I? Display your current profile, access key, and links to your satellite. ``` Usage: juno whoami [options]Options: -h, --help Output usage information. -m, --mode Set env mode. For example production or a custom string. Default is production. --container-url Override a custom container URL. If not provided, defaults to production or the local container in development mode. --console-url Specify a custom URL to access the developer Console. ``` # Configuration When the `juno` command is run from your terminal or used in a CI environment, it will automatically attempt to locate a config file named `juno.config.ts` or `juno.config.js` or `juno.config.json` within your project's root directory. The Juno config file defines the settings and options needed for managing and deploying your satellites and other project modules, ensuring consistency across environments. A basic config file looks like this: ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "build" }, orbiter: { id: "eeeee-fffff-ddddd-11111-cai" }}); ``` At the top level, the Juno configuration includes two main sections: 1. `satellite` (required): This defines the behavior of your satellite. 2. `orbiter` (optional): This is useful to automatically map your analytics in your application. **Important:** To apply any changes you make in your configuration, execute the [juno config](/docs/reference/cli.md#config) command with the CLI. --- ## Satellite Configuration Satellites are the core component of your application in Juno. The satellite configuration defines how the satellite operates, including its identifier, datastore, storage, authentication, and more. This configuration is crucial for managing the satellite’s behavior and deploying it across different environments. The satellite configuration is defined in your Juno configuration file. Below is a detailed explanation of how to configure it. ### ID or IDs Each satellite must be uniquely identified using either: * `id`: A single identifier for the satellite. * `ids`: A mapping of identifiers for multiple environments, such as staging or production. You can use one of these options but not both simultaneously. See the ([Environments - Multiple Satellites](#environments---multiple-satellites)) chapter below for details on setting up multiple ids. **Tip:** If you are using a framework like Next.js or Vite, Juno provides plugins to simplify loading Satellite and Orbiter IDs from your configuration file. These plugins automatically handle environment variable management and initialization. * [Next.js Plugin Documentation](/docs/reference/plugins.md#nextjs-plugin) * [Vite Plugin Documentation](/docs/reference/plugins.md#vite-plugin) ### Source The `source` field specifies the directory that contains the built assets for your satellite. This is typically the output directory generated by your build process after running a command like `npm run build`. Commonly, or if you are using the templates, these are the folders that can be set as the `source` field: | Framework | Source | | --- | --- | | Next.js | `out` | | React, Astro, or Vue | `dist` | | SvelteKit | `build` | | Angular | `dist//browser` | Juno uses this directory to locate the files that will be deployed as part of your satellite. Ensure that this directory includes all the necessary assets, such as HTML, JavaScript, CSS, and any other static or dynamic resources your application requires. juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist" }}); ``` ### Ignore files The `ignore` attribute allows you to exclude certain files from being deployed to your satellite. This attribute works similarly to Git's `.gitignore`, and you can specify which files to ignore using globs. Here is an example of how the ignore attribute can be utilized: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", ignore: ["**/*.txt", ".tmp/"] }}); ``` ### GZIP When deploying your application, the CLI automatically searches for JavaScript (js), ES Module (mjs), and CSS (css) files in the `source` folder and optimizes them using Gzip compression. This is useful because neither the protocol nor a satellite can compress these files, ensuring the best web performance. If you wish to customize this behavior, you have the option to disable it or provide a different file matching pattern using glob syntax. To opt-out of Gzip compression, simply set the `gzip` option to `false` in your configuration: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", gzip: false }}); ``` If you want to customize the default pattern `**/*.+(css|js|mjs)` to better suit your needs, you can specify your own pattern. For example: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", gzip: "**/*.jpg" }}); ``` ### Encoding When deploying, the CLI automatically maps the encoding type based on the file extension. The encoding information is then used in the satellite to provide the appropriate HTTP response header `Content-Encoding`. The default mappings are as follows: * `.Z` = `compress` * `.gz` = `gzip` * `.br` = `br` * `.zlib` = `deflate` * rest = `identity` (no compression) You can also customize the encoding behavior by using the "encoding" attribute in the configuration file. This attribute works similarly to Git's `.gitignore`, and you can specify which files to ignore using globs. Here is an example of how the "encoding" attribute can be utilized: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", encoding: [["**/releases/*.gz", "identity"]] }}); ``` ### Predeploy The predeploy option allows you to define a list of scripts or commands to be executed before the deployment process begins. This is particularly useful for automating tasks such as: * Compiling assets. * Running tests or linters. * Preparing production-ready files. These scripts are executed sequentially in the order they are listed. ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", predeploy: ["npm run build", "npm run lint"] }}); ``` ### Postdeploy The postdeploy option allows you to define a list of scripts or commands to be executed after the deployment process completes. This can be used for various follow-up tasks, such as: * Sending notifications or alerts to administrators. * Cleaning up temporary files. * Logging deployment information for auditing. Like `predeploy`, these scripts are executed sequentially in the order they are listed. ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", postdeploy: ["./scripts/notify-admins.sh", "echo 'Deployment complete'"] }}); ``` ### Storage The `storage` configuration accepts the following options and parameters: #### HTTP Headers Headers allow the client and the satellite to pass additional information along with a request or a response. Some sets of headers can affect how the browser handles the page and its content. For instance, you may want to set a specific `Cache-Control` for performance reasons. Here's an example of the `headers` object: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", storage: { headers: [ { source: "/", headers: [["Cache-Control", "public,max-age=0,must-revalidate"]] }, { source: "assets/fonts/*", headers: [["Cache-Control", "max-age=31536000"]] }, { source: "**/*.jpg", headers: [ ["Cache-Control", "max-age=31536000"], ["Access-Control-Allow-Origin", "*"] ] } ] } }}); ``` This `source` attribute works similarly to Git's `.gitignore`, and you can specify which files match the headers using globs. The `headers` is an array of objects, each containing `key` and `value`, and these apply to the matching paths. **Note:** * The `Content-Type` header is calculated automatically and cannot be altered. * No validation or check for uniqueness is performed. For example, if a header matches a file based on multiple rules, multiple headers will be set. * Likewise, if you provide the same header when you [upload](https://juno.build/docs/build/storage#upload-asset) file to your "Storage" and within the configuration, both headers will be set in the response. #### Rewrites You can utilize optional rewrites to display the same content for multiple URLs. Rewrites are especially useful when combined with pattern matching, allowing acceptance of any URL that matches the pattern. Here's the basic structure for a `rewrites` attribute. juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", storage: { rewrites: [ { source: "/hello/**", destination: "/hello/world.html" } ] } }}); ``` This `source` attribute works similarly to Git's `.gitignore`, and you can specify which files match the rewrites using globs. **Note:** * Rewrites are only applied to requests that do not match any existing resources. * By default, all unknown paths are automatically rewritten to `/index.html` (or `/404.html` if you provide such a page). You cannot disable this default behavior. #### Redirects Use a URL redirect to prevent broken links if you've moved a page or to shorten URLs. For example, you could redirect a browser from `juno.build/start-building` to `juno.build/get-started.html`. Here's the basic structure for a `redirects` attribute. juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", storage: { redirects: [ { source: "/hello", location: "/world/index.html", code: 301 } ] } }}); ``` The `redirects` attribute contains an array of redirect rules: | Field | Description | | --- | --- | | **source** | This `source` attribute works similarly to Git's `.gitignore`, and you can specify which files match the redirects using globs. | | **location** | A relative path to where the browser should make a new request. | | **code** | The HTTPS response code. Use a type of `301` for 'Moved Permanently' or `302` for 'Found' (Temporary Redirect). | #### iframe For security reasons and to prevent click-jacking attacks, dapps deployed with Juno are, by default, set to deny embedding in other sites. You can customize this behavior by setting the `iframe` option to either `same-origin`, which restricts your pages to be displayed only if all ancestor frames have the same origin as the page itself, or `allow-any`, which allows your project to be embeddable by any site. juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", storage: { iframe: "same-origin" } }}); ``` #### Maximum Memory Size You can configure optional limits on heap and stable memory for your smart contract to control the creation and update of assets in your storage. When the limit is reached, the Storage and smart contract will continue to operate normally but will reject the upload of new assets. ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", storage: { maxMemorySize: { stable: 1_073_741_824n // For example max. 1 GiB in bytes of Stable memory } } }}); ``` ### Datastore The `datastore` configuration accepts the following options and parameters: #### Maximum Memory Size You can configure optional limits on heap and stable memory for your smart contract to control the creation and update of documentations in your Datastore. When the limit is reached, the Datastore and smart contract will continue to operate normally but will reject changes to documents. ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", datastore: { maxMemorySize: { stable: 1_073_741_824n // For example max. 1 GiB in bytes of Stable memory } } }}); ``` ### Authentication The `authentication` configuration accepts the following options and parameters: #### Derivation origin The behavior of Internet Identity can be customized to ensure that users are recognized consistently across different domains or subdomains of your application. For example, if you set `derivationOrigin` to "hello.com", a user signing in at [https://hello.com](https://hello.com) will receive the same identifier (principal) as when signing in at [https://www.hello.com](https://www.hello.com). ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", authentication: { internetIdentity: { derivationOrigin: "hello.com" } } }}); ``` ### Assertions The CLI conducts several assertions when interacting with your Satellite, one of which involves monitoring the heap memory size. Typically, the CLI checks to ensure that the heap memory does not exceed the 1 GB limit before deployment. For instance, if your heap memory usage is close to 900 MB, the CLI will prompt you to confirm the deployment. You can customize this behavior by adjusting the heap memory limit in bytes. For example, to set a new limit of 678 MB, update your configuration as follows: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", assertions: { heapMemory: 678000000 } }}); ``` Alternatively, these checks can be completely disabled. To do so, set the `heapMemory` assertion to `false`: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist", assertions: { heapMemory: false } }}); ``` ### Settings The `settings` field allows you to configure various aspects of the module’s behavior and resource usage, such as memory limits, compute allocation, and log visibility. Overview: * **Freezing Threshold**: Prevents the module from being deleted by pausing operations when cycles drop below a certain level. * **Reserved Cycles Limit**: Ensures a minimum number of cycles are available for future operations. * **Log Visibility**: Controls who can access the module’s logs. * **Heap Memory Limit**: Sets the hard maximum heap memory available to the module. * **Memory Allocation**: Pre-allocates memory for optimized performance. * **Compute Allocation**: Reserves a percentage of the subnet’s compute resources. ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", settings: { freezingThreshold: 2_592_000n, reservedCyclesLimit: 5_000_000_000_000n, logVisibility: "controllers", heapMemoryLimit: 2048n, memoryAllocation: 1_073_741_824n, computeAllocation: 50n } }}); ``` For a complete explanation of all [settings](/docs/reference/settings.md), including detailed examples and calculations, see the Settings section. --- ## Orbiter Configuration Orbiters are an optional component of your application used for analytics. ### ID An orbiter has a unique identifier (id). This ID is used to reference the orbiter during operations and deployments. **Tip:** If you are using a framework like Next.js or Vite, Juno provides plugins to simplify loading Satellite and Orbiter IDs from your configuration file. These plugins automatically handle environment variable management and initialization. * [Next.js Plugin Documentation](/docs/reference/plugins.md#nextjs-plugin) * [Vite Plugin Documentation](/docs/reference/plugins.md#vite-plugin) --- ## Emulator Configuration For local development or when running an E2E test environment in a CI, you'll use the emulator. When running `juno dev start`, it defaults to booting Skylab with the default options. If you wish to tweak options or use another image, you can set the `emulator` field in your configuration file. A basic example of a custom emulator configuration: juno.config.ts ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist" }, emulator: { runner: { type: "docker", image: "junobuild/skylab:latest", name: "juno-skylab", volume: "juno", platform: "linux/amd64" }, skylab: { ports: { server: 5987, admin: 5999, console: 5866 } } }}); ``` ### Runner Options The `runner` field allows you to customize how the emulator container is run. | Option | Required | Description | Default | | --- | --- | --- | --- | | type | ✅ | Container runtime used. Supports `docker` and `podman`. | `docker` | | image | | The image used to start the emulator. | `junobuild/skylab:latest` | | name | | Custom name for the container instance. | Derived from your `package.json` field `name` or falling on `juno-image` | | volume | | A volume used to persist internal state - saves the data across sessions. | The container name adapted (replace `-` by `_`) | | target | | Shared folder for deploying and hot-reloading serverless functions. | `$PWD/target/deploy` | | platform | | Platform to run the emulator on (`linux/amd64` or `linux/arm64`). | Automatic | **Note:** When defining a `runner`, you must also set the corresponding image configuration (`skylab`, `satellite`, or `console`), which can be left empty. This ensures the emulator knows which component to launch. #### Example using Podman If you're using Podman instead of Docker, you can specify it with the `type` field: juno.config.ts ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { id: "qsgjb-riaaa-aaaaa-aaaga-cai", source: "dist" }, emulator: { runner: { type: "podman" }, skylab: {} }}); ``` This will use the default image and settings, but with Podman as the container runtime. You can customize the image, volume, and other options just like you would with Docker. ### Emulator You can choose from three emulator types, depending on which part of the stack you want to run locally: * `skylab`: A full emulator stack including the Console UI. * `console`: A minimal emulator for Console-only scenarios. * `satellite`: Runs just a Satellite container for focused development. Each type has its own `ports` configuration. #### Ports You can customize the ports exposed by the emulator: | Field | Description | Default | | --- | --- | --- | | server | The local Internet Computer replica port. Your app or project interacts with it. | `5987` | | admin | Admin server used for internal tasks (e.g., ICP transfer). | `5999` | | console | Console UI port (only used with `skylab`). | `5866` | --- ## Apply Changes Configurations such as above ([storage](#storage)), ([datastore](#datastore)), ([authentication](#authentication)), and ([settings](#settings)) require explicit application to your smart contract as they directly impact its behavior. To apply your changes, run the [juno config](/docs/reference/cli.md#config) command in the CLI after modifying your configuration file. --- ## Intellisense To enable intellisense in your IDE for TypeScript or JavaScript configurations, you will need to install the necessary types: ``` npm install @junobuild/config --save-dev ``` Afterwards, you can leverage your IDE's intellisense with jsdoc type hints: juno.config.js ``` /** @type {import('@junobuild/config').JunoConfig} */export default { // ...}; ``` Alternatively, you can use the `defineConfig` helper which should provide intellisense without the need for jsdoc annotations: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ // ...}); ``` --- ## Conditional Config If the config needs to conditionally determine options based the `mode` being used, it can export a function instead: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig(({ mode }) => ({ satellite: { id: "aaaaa-bbbbb-ccccc-ddddd-cai", source: "dist", storage: { ...(mode === "staging" && { iframe: "allow-any" }) }, predeploy: [`npm run build -- --mode ${mode}`] }})); ``` --- ## Modes By default, the CLI runs command for the `production` mode. This means when running a `juno` command in your terminal, it will pass the mode `production` to read your configuration. You can overwrite the default mode used for a command by passing the `--mode` option flag. For example, if you want to deploy your app for a `staging` mode: ``` juno deploy --mode staging ``` --- ## Environments - Multiple satellites You might want to deploy or manage a similar project across different environments. Using ([modes](#modes)), you can deploy the same application on multiple satellites, such as staging and production environments. To accommodate different satellite IDs, you can use a ([conditional config](#conditional-config)): juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig(({ mode }) => ({ satellite: { id: mode === "staging" ? "11111-22222-33333-44444-cai" : "aaaaa-bbbbb-ccccc-ddddd-cai", source: "dist" }})); ``` Or explicitly list the IDs: juno.config.js ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { ids: { staging: "11111-22222-33333-44444-cai", production: "aaaaa-bbbbb-ccccc-ddddd-cai" }, source: "dist" }}); ``` The latter method is also compatible with JSON configuration: juno.config.json ``` { "satellite": { "ids": { "staging": "11111-22222-33333-44444-cai", "production": "aaaaa-bbbbb-ccccc-ddddd-cai" }, "source": "dist" }} ``` Note that defining an `id` or at least a `production` entry in `ids` is mandatory. # Emulator The emulator provides a complete local environment to build, test, and run your project without deploying anything live. There are two images available, depending on your needs: [## 📄️ Skylab The junobuild/skylab image is an all-in-one emulator for local development. It bundles everything you need to build, test, and explore the Juno ecosystem:](/docs/reference/emulator/skylab.md) [## 📄️ Satellite Unlike Skylab, the image junobuild/satellite runs a single Satellite in a sandboxed local environment.](/docs/reference/emulator/satellite.md) [## 📄️ Infrastructure In the local environment, several modules (also known as "canisters" on the Internet Computer) are automatically spun up. This ensures that developers have everything they need to start building right out of the box. Thanks to built-in plugins and tooling, these modules are automatically integrated into the environment, eliminating the need for devs to manually manage their bindings.](/docs/reference/emulator/infrastructure.md) # Functions API reference for writing serverless functions in Rust or TypeScript. [## 🗃️ Rust 3 items](/docs/reference/functions/rust.md) [## 🗃️ TypeScript 4 items](/docs/reference/functions/typescript.md) # Plugins Juno provides various plugins to simplify your development workflow. We also warmly welcome community contributions. If you would like to create or submit plugins or any libraries, please reach out or explore our [repository](https://github.com/junobuild/plugins)! --- ## Next.js Plugin If you are developing your app using Next.js, this plugin automatically loads Satellite and Orbiter IDs from your project's configuration file. These values allow you to instantiate Juno in your code without the need to manually define environment variables. ``` // Init a Satelliteawait initSatellite();// Init the analyticsinitOrbiter(); ``` However, if you wish to explicitly use the environment variables that are loaded by the plugin, you can do so. This is notably required if you specify a prefix other than `NEXT_PUBLIC_`. ``` // Init a Satelliteawait initSatellite({ satelliteId: process.env.NEXT_PUBLIC_SATELLITE_ID });// Init the analyticsinitOrbiter({ satelliteId: process.env.NEXT_PUBLIC_SATELLITE_ID, orbiterId: process.env.NEXT_PUBLIC_ORBITER_ID}); ``` ### Installation Add it to your dev dependencies with: * npm * yarn * pnpm ``` npm i @junobuild/nextjs-plugin -D ``` ``` yarn add @junobuild/nextjs-plugin -D ``` ``` pnpm add @junobuild/nextjs-plugin -D ``` ### Usage In your Next.js config file — whether it's `next.config.js`, `next.config.ts`, `next.config.mjs` or else — wrap your configuration with `withJuno` to automatically load Juno settings: ``` import { withJuno } from "@junobuild/nextjs-plugin";export default withJuno(); ``` The plugin sets the build output to `export` by default. You can override the option or provide additional options as follows: ``` import { withJuno } from "@junobuild/nextjs-plugin";/** @type {import('next').NextConfig} */const nextConfig = { output: "export"};export default withJuno({ nextConfig }); ``` In other words, if you want to include additional Next.js configuration (e.g. `i18n`, `env` etc.), just define them in your `nextConfig` object and pass it to `withJuno`. ### Local development By default, the plugin supports local development and loads environment variables accordingly. You can use the `container` option to: * Provide a custom container URL (e.g. for an emulator running on a specific port), or * Set it to `false` to disable local development behavior entirely. next.config.mjs ``` import { withJuno } from "@junobuild/nextjs-plugin";/** @type {import('next').NextConfig} */const nextConfig = { output: "export"};export default withJuno({ nextConfig, juno: { container: false } }); ``` ### More information Discover additional information in the library's [README](https://github.com/junobuild/plugins/tree/main/plugins/nextjs-plugin). --- ## Vite Plugin If you are developing your app using Vite, this plugin automatically loads the Satellite ID from your project's configuration file. If you are using analytics, it also loads the Orbiter ID too. These values allow you to instantiate Juno in your code without the need to manually define environment variables. ``` // Init a Satelliteawait initSatellite();// Init the analyticsinitOrbiter(); ``` However, if you wish to explicitly use the environment variables that are loaded by the plugin, you can do so. This is notably required if you specify a prefix other than the default, such as `VITE_` or `PUBLIC_`. ``` // Init a Satelliteawait initSatellite({ satelliteId: import.meta.env.VITE_SATELLITE_ID });// Init the analyticsinitOrbiter({ satelliteId: import.meta.env.VITE_SATELLITE_ID, orbiterId: import.meta.env.VITE_ORBITER_ID}); ``` ### Installation Add it to your dev dependencies with: * npm * yarn * pnpm ``` npm i @junobuild/vite-plugin -D ``` ``` yarn add @junobuild/vite-plugin -D ``` ``` pnpm add @junobuild/vite-plugin -D ``` ### Usage Add the plugin to your Vite configuration — whether you're using TypeScript or JavaScript — to automatically load Juno settings: vite.config.js ``` import juno from "@junobuild/vite-plugin";export default defineConfig({ plugins: [juno()]}); ``` ### Local development By default, the plugin supports local development and loads environment variables accordingly. You can use the `container` option to: * Provide a custom container URL (e.g. for an emulator running on a specific port), or * Set it to `false` to disable local development behavior entirely. vite.config.js ``` import juno from "@junobuild/vite-plugin";export default defineConfig({ plugins: [ juno({ container: false }) ]}); ``` ### More information Discover additional options in the library's [README](https://github.com/junobuild/plugins/tree/main/plugins/vite-plugin). # Settings This document will help you understand the different settings you can configure for your modules ([Satellites](/docs/terminology.md#satellite), [Mission controls](/docs/terminology.md#mission-control), and [Orbiters](/docs/terminology.md#orbiter)). --- ## Freezing Threshold The Freezing Threshold defines the duration (in seconds) after which a module will be frozen. It is an important feature because if a module runs out of cycles, it will be uninstalled, meaning its code and state are deleted. The Freezing Threshold protects from deletion. If the cycles balance dips below the threshold, the smart contract will stop processing any new requests but will continue to reply to existing requests. For sensitive applications that requires several resources, developers can set a freezing threshold to 90 days or more. This ensures that they and their users have enough time to react and top up the modules before they finally run out of cycles. The default value is `2_592_000n` (30 days). ### In Other Words To remain active and able to respond to state-changing requests—like updating data—a module must maintain a cycles balance that covers the cost of the freezing threshold period. For example, if a module currently has 1 TCycles but needs 5 TCycles to stay alive for the 30-day freezing threshold, it will be considered frozen. If it is topped up to 5.1 TCycles, it becomes active again and can resume processing requests—until that extra 0.1 TCycles is consumed. ### Example of calculation We want to calculate how many cycles are required to keep a module from being frozen, based on the freezing threshold and idle cycle burn rate. Let’s say: * **Freezing Threshold**: 2,592,000 seconds (30 days) * **Idle Cycles Burned per Day**: 0.01 T Cycles (i.e. 10,000,000,000 cycles) To compute the required cycles for the freezing threshold: ``` required_cycles = (idle_cycles_burned_per_day × freezing_threshold_seconds) / 86,400 ``` > 86,400 is the number of seconds in one day Substitute the values: ``` required_cycles = (10,000,000,000 × 2,592,000) / 86,400= 25,920,000,000,000,000 / 86,400= 300,000,000,000 cycles= 0.3 T Cycles ``` Result ✅: With a freezing threshold of 30 days and an idle burn rate of 0.01 T Cycles per day, the module must have a cycles balance greater than 0.3 T Cycles to avoid being frozen and to continue processing update requests. --- ## Reserved Cycles Limit The Reserved Cycles Limit sets the maximum number of cycles a module can reserve. If the reserved cycles exceed this limit, any operation requiring resources, such as compute or memory, will fail. The default value is `5_000_000_000_000n` (5 trillion cycles) ### Example A practical use case could be a scenario where a module is expected to handle a large amount of data storage or perform intensive computations. By setting the Reserved Cycles Limit, developers can control the maximum amount of cycles that can be reserved for the future resource payments. This helps in preventing the smart contract from exceeding its allocated budget and also ensures that it has enough cycles for its operations. --- ## Log Visibility This setting controls who can see the logs of the smart contract. It can be set to different levels of visibility, such as `public` or restricted to `controllers`. You can find the logs in the "Functions" section of Juno's administration Console. The default value is `controllers`. **Tip:** On Juno, an administrative access key is a controller. ### Example If you set log visibility to `public`, anyone can view the logs of the module. --- ## Heap Memory Limit This setting defines the maximum amount of `heap` memory a module can use. It helps in controlling the memory usage of the module and prevents issues when upgrading. That is why the default limit for Satellites is set to 1 GB. ### Example If you set the heap memory limit to `2048n`, the module can use up to 2 GiB. Note, however, as mentioned above, that we assume the effective limit to ensure upgrades lies around 1 GiB. --- ## Memory Allocation This setting specifies the amount of memory that is pre-allocated to the module. Pre-allocating memory can help in optimizing the smart contract's performance by ensuring it has a guaranteed amount of memory available from the start. The default value is `0n` - i.e. no particular pre-allocation. ### Example If you set memory allocation to `1_073_741_824n` (1 GiB), the module will have 1 GiB of memory allocated to it from the start. This ensures that the module has sufficient memory pre-allocated for its operations, potentially improving its performance by reducing the need for dynamic memory allocation during execution. --- ## Compute Allocation This setting defines the percentage of compute capacity allocated to the module. It ensures that the module gets a certain share of compute resources. The default value is `0n` - i.e. no particular allocation. ### Example If you set the compute allocation to `50n`, the module will be allocated 50% of the compute capacity. This ensures that the module has a guaranteed share of the compute resources, potentially improving its performance by ensuring it has sufficient processing power for its operations. # Infrastructure In the local environment, several modules (also known as "canisters" on the Internet Computer) are automatically spun up. This ensures that developers have everything they need to start building right out of the box. Thanks to built-in plugins and tooling, these modules are automatically integrated into the environment, eliminating the need for devs to manually manage their bindings. However, in some cases, it may be useful to explicitly reference module IDs. Below is a list of the modules and their respective IDs that are automatically mounted. | Module | ID | | --- | --- | | [Internet Identity](https://dashboard.internetcomputer.org/canister/rdmx6-jaaaa-aaaaa-aaadq-cai) | `rdmx6-jaaaa-aaaaa-aaadq-cai` | | [ICP Ledger](https://dashboard.internetcomputer.org/canister/ryjl3-tyaaa-aaaaa-aaaba-cai) | `ryjl3-tyaaa-aaaaa-aaaba-cai` | | [ICP Index](https://dashboard.internetcomputer.org/canister/qhbym-qaaaa-aaaaa-aaafq-cai) | `qhbym-qaaaa-aaaaa-aaafq-cai` | | [CMC](https://dashboard.internetcomputer.org/canister/rkp4c-7iaaa-aaaaa-aaaca-cai) | `rkp4c-7iaaa-aaaaa-aaaca-cai` | | [NNS Governance](https://dashboard.internetcomputer.org/canister/rrkah-fqaaa-aaaaa-aaaaq-cai) | `rrkah-fqaaa-aaaaa-aaaaq-cai` | # Satellite Unlike Skylab, the image [junobuild/satellite](https://hub.docker.com/r/junobuild/satellite) runs a single Satellite in a sandboxed local environment. You can configure the behavior of Satellite with a specific configuration file to define Datastore and Storage collections, additional administrative access keys, and optional serverless extensions. Like Skylab, it also supports live reloading for these serverless functions through a shared folder. The CLI watches configuration files and a dedicated `deploy` folder, automatically applying changes and upgrading modules as needed. --- ## Configuration The behavior of the Satellite running in a container can be configured with the help of a local configuration file commonly named `juno.dev.config.ts` (or JavaScript or JSON). This configuration file enables you to define the collections of the Datastore and Storage that run locally, but it also allows for defining additional controllers - i.e. administrative access keys - for your satellite. The definition is as follows: ``` export type PermissionText = "public" | "private" | "managed" | "controllers";export type MemoryText = "heap" | "stable";export type RulesType = "db" | "storage";export interface Rule { collection: string; read: PermissionText; write: PermissionText; memory: MemoryText; createdAt?: bigint; updatedAt?: bigint; maxSize?: number; maxCapacity?: number; mutablePermissions: boolean; maxTokens?: number;}export type SatelliteDevDbCollection = Omit< Rule, "createdAt" | "updatedAt" | "maxSize">;export type SatelliteDevStorageCollection = Omit< Rule, "createdAt" | "updatedAt" | "maxCapacity">;export interface SatelliteDevCollections { db?: SatelliteDevDbCollection[]; storage?: SatelliteDevStorageCollection[];}export interface SatelliteDevController { id: string; scope: "write" | "admin";}export interface SatelliteDevConfig { collections: SatelliteDevCollections; controllers?: SatelliteDevController[];}export interface JunoDevConfig { satellite: SatelliteDevConfig;} ``` ### Example If, for example, we want to configure a "metadata" collection in the Datastore, a "content" collection in the Storage, and provide an additional controller, we could use the following configuration: juno.dev.config.json ``` { "satellite": { "collections": { "db": [ { "collection": "metadata", "read": "managed", "write": "managed", "memory": "stable", "mutablePermissions": true } ], "storage": [ { "collection": "content", "read": "public", "write": "public", "memory": "stable", "mutablePermissions": true } ] }, "controllers": [ { "id": "535yc-uxytb-gfk7h-tny7p-vjkoe-i4krp-3qmcl-uqfgr-cpgej-yqtjq-rqe", "scope": "admin" } ] }} ``` ### More Options For more advanced options like customizing ports, image name, or CI setup, see the [Emulator Configuration](/docs/reference/configuration.md#emulator-configuration) section. # Skylab The [junobuild/skylab](https://hub.docker.com/r/junobuild/skylab) image is an all-in-one emulator for local development. It bundles everything you need to build, test, and explore the Juno ecosystem: * ✅ Juno Console (smart contract + UI) * 🛰️ Satellites (support for multiple application containers) * 📊 Orbiter (analytics and tracking module) * ⚙️ Supporting infrastructure (see table below) This container mounts an [Internet Computer](https://internetcomputer.org/) Replica and `icx-proxy` within a sandbox. Once ready, a custom-built CLI takes care of deploying and setting up the modules during the first boot. It also actively watches a shared folder, allowing you to live reload serverless functions written in Rust or TypeScript. This container replicates the production experience locally. That's why, when building your project with it, you'll need to create your Satellites for testing through the Console UI, just as you would in production. --- ## Configuration Skylab requires minimal configuration. Since the Console UI is used to create your Satellite, the only step is to reference the correct Satellite ID in your project’s config file. You can specify this ID under the `development` key in your Juno Config file: juno.config.ts ``` import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { ids: { development: "", production: "" }, source: "dist", predeploy: ["npm run build"] }}); ``` For more advanced options like customizing ports, image name, or CI setup, see the [Emulator Configuration](/docs/reference/configuration.md#emulator-configuration) section. --- ## Accessing the Console UI When running Skylab, the Console UI is available at [http://localhost:5866](http://localhost:5866). This is the same interface used in production at [console.juno.build](https://console.juno.build), allowing you to create Satellites and Orbiters, inspect your wallet, manage your Datastore and Storage, etc. # Rust API reference for writing serverless functions in Rust. [## 📄️ SDK The SDK is provided by the junobuild-satellite crate.](/docs/reference/functions/rust/sdk.md) [## 📄️ Utils All utilities on this page are provided by the junobuild-utils crate.](/docs/reference/functions/rust/utils.md) [## 📄️ IC-CDK In the context of Juno, it enables your Satellite to perform low-level operations such as logging, accessing your Satellite identities, or communicating with other canisters — all essential when writing advanced serverless functions.](/docs/reference/functions/rust/ic-cdk.md) # TypeScript API reference for writing serverless functions with TypeScript. [## 📄️ SDK The SDK is provided by the @junobuild/functions library.](/docs/reference/functions/typescript/sdk.md) [## 📄️ Utils All utilities on this page are provided by the @junobuild/functions library.](/docs/reference/functions/typescript/utils.md) [## 📄️ IC-CDK Juno exposes a growing set of these features for TypeScript, allowing you to build serverless functions that interact with the IC using a familiar developer experience.](/docs/reference/functions/typescript/ic-cdk.md) [## 📄️ Node.js The TypeScript runtime used in Juno does not provide full Node.js support. Polyfills are added iteratively to keep the environment stable and predictable.](/docs/reference/functions/typescript/node.md) # IC-CDK The [Canister Development Kit](https://github.com/dfinity/cdk-rs) (`ic-cdk` or `ic_cdk`) provides core functionality for interacting with the Internet Computer in Rust. In the context of Juno, it enables your Satellite to perform low-level operations such as logging, accessing your Satellite identities, or communicating with other canisters — all essential when writing advanced serverless functions. **All features** of the IC CDK are supported in Juno Satellites. Because of this compatibility, we do not list them individually here and encourage you to consult the official documentation instead. 📦 See full documentation on [docs.rs/ic-cdk](https://docs.rs/ic-cdk/latest/ic_cdk/) **Note:** For compatibility, always use the `ic_cdk` version specified in Juno’s release notes. This ensures proper integration and avoids version mismatch issues. # SDK The following functions are provided to help you work with document and asset data inside your Satellite. They are part of the tools available when writing serverless functions in Rust and support common tasks such as interacting with the datastore, storage, and custom hook logic. **📦 Crate:** The SDK is provided by the [junobuild-satellite](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/index.html) crate. To use them, add this to your Cargo.toml: ``` [dependencies]junobuild-satellite = "*" ``` You have to follow the pace of the Juno release to ensure compatibility. Refer to the [maintenance guide](/docs/build/functions/development/rust.md#maintenance) for instructions. --- ## Datastore The following functions can be used to manage documents within the Datastore from your serverless functions. --- ### set\_doc\_store Sets a document in a collection’s of the datastore. Use this to insert or update document data. ``` pub fn set_doc_store( caller: UserId, collection: CollectionKey, key: Key, value: SetDoc,) -> Result, String> ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.set_doc_store.html) --- ### get\_doc\_store Retrieves a document from a collection. ``` pub fn get_doc_store( caller: UserId, collection: CollectionKey, key: Key,) -> Result, String> ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.get_doc_store.html) --- ### list\_docs\_store Lists documents in a collection based on filter criteria. ``` pub fn list_docs_store( caller: Principal, collection: CollectionKey, filter: &ListParams,) -> Result, String> ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.list_docs_store.html) --- ### count\_docs\_store Counts documents in a collection based on filter criteria. ``` pub fn count_docs_store( caller: Principal, collection: CollectionKey, filter: &ListParams,) -> Result ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.count_docs_store.html) --- ### count\_collection\_docs\_store Counts all documents in a collection. ``` pub fn count_collection_docs_store( collection: &CollectionKey) -> Result ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.count_collection_docs_store.html) --- ### delete\_doc\_store Deletes a document from a collection. ``` pub fn delete_doc_store( caller: UserId, collection: CollectionKey, key: Key, value: DelDoc,) -> Result>, String> ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.delete_doc_store.html) --- ### delete\_docs\_store Deletes all documents in a collection. ``` pub fn delete_docs_store( collection: &CollectionKey) -> Result<(), String> ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.delete_docs_store.html) --- ### delete\_filtered\_docs\_store Deletes documents matching filter criteria. ``` pub fn delete_filtered_docs_store( caller: Principal, collection: CollectionKey, filter: &ListParams,) -> Result>>, String> ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.delete_filtered_docs_store.html) --- ## Storage The following functions can be used to manage assets within the Storage from your serverless functions. --- ### set\_asset\_handler Sets an asset in the store for the identity encoding (no compression applied). ``` pub fn set_asset_handler( key: &AssetKey, content: &Blob, headers: &[HeaderField],) -> Result<(), String> ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.set_asset_handler.html) --- ### get\_asset\_store Retrieves an asset from a collection. ``` pub fn get_asset_store( caller: Principal, collection: &CollectionKey, full_path: FullPath,) -> Result, String> ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.get_asset_store.html) --- ### list\_assets\_store Lists assets in a collection, excluding their content. ``` pub fn list_assets_store( caller: Principal, collection: &CollectionKey, filters: &ListParams,) -> Result, String> ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.list_assets_store.html) --- ### count\_assets\_store Counts assets in a collection matching the filter criteria. ``` pub fn count_assets_store( caller: Principal, collection: &CollectionKey, filters: &ListParams,) -> Result ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.count_assets_store.html) --- ### count\_collection\_assets\_store Counts all assets in a collection. ``` pub fn count_collection_assets_store( collection: &CollectionKey,) -> Result ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.count_collection_assets_store.html) --- ### delete\_asset\_store Deletes an asset from a collection. ``` pub fn delete_asset_store( caller: Principal, collection: &CollectionKey, full_path: FullPath,) -> Result, String> ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.delete_asset_store.html) --- ### delete\_assets\_store Deletes all assets in a collection. ``` pub fn delete_assets_store( collection: &CollectionKey,) -> Result<(), String> ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.delete_assets_store.html) --- ### delete\_filtered\_assets\_store Deletes assets matching filter criteria. ``` pub fn delete_filtered_assets_store( caller: Principal, collection: CollectionKey, filters: &ListParams,) -> Result>, String> ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.delete_filtered_assets_store.html) --- ### get\_content\_chunks\_store Retrieves content chunks of an asset. Particularly useful when stable memory is used. ``` pub fn get_content_chunks_store( encoding: &AssetEncoding, chunk_index: usize, memory: &Memory,) -> Option ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.get_content_chunks_store.html) --- ## Controllers The following functions allow you to inspect and assert the controllers of your Satellite. --- ### get\_controllers Retrieves all controllers of the Satellite. ``` pub fn get_controllers() -> Controllers ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.get_controllers.html) --- ### get\_admin\_controllers Retrieves only the admin controllers of the Satellite. ``` pub fn get_admin_controllers() -> Controllers ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.get_admin_controllers.html) --- ## Others The SDK also provides various Satellite-specific functions --- ### random Generates a random `i32`. **Caution:** The generator is seeded once after upgrade of the Satellite. This makes it unsuitable for use cases requiring unpredictable randomness, like lotteries. ``` pub fn random() -> Result ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-satellite/latest/junobuild_satellite/fn.random.html) # Utils The following utilities are provided to help you work with document and asset data inside your Satellite. They simplify tasks such as decoding and encoding data, serializing custom types, and interacting with Juno’s core features in a consistent way. **📦 Crate:** All utilities on this page are provided by the [junobuild-utils](https://docs.rs/junobuild-utils/latest/junobuild_utils/index.html) crate. To use them, add this to your Cargo.toml: ``` [dependencies]junobuild-utils = "*" ``` Replace `*` with the specific version you want to use, or omit the version to always use the latest version. --- ## decode\_doc\_data Deserializes raw document data (`&[u8]`) into a typed Rust struct. Use this inside hooks or assertions to get the document contents in a strongly typed way. ``` pub fn decode_doc_data Deserialize<'a>>( data: &[u8],) -> Result ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-utils/latest/junobuild_utils/fn.decode_doc_data.html) --- ## encode\_doc\_data Serializes a Rust struct into a `Vec` for storing a document into the datastore. Use this when modifying or creating document data inside a hook or assertion. ``` pub fn encode_doc_data(data: &T) -> Result, String> ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-utils/latest/junobuild_utils/fn.encode_doc_data.html) --- ## encode\_doc\_data\_to\_string Serializes a Rust struct into a JSON String. Use this if you want to store or inspect document data in a readable format. Commonly used when exposing JSON data on the web, for example by reproducing documents from the datastore into the storage. ``` pub fn encode_doc_data_to_string( data: &T,) -> Result ``` 📦 See full definition on [docs.rs](https://docs.rs/junobuild-utils/latest/junobuild_utils/fn.encode_doc_data_to_string.html) # IC-CDK The [Canister Development Kit](https://github.com/dfinity/cdk-rs) (`ic-cdk` or `ic_cdk`) provides core functionality for interacting with the Internet Computer in Rust. Juno exposes a growing set of these features for TypeScript, allowing you to build serverless functions that interact with the IC using a familiar developer experience. These features are made available through a layer that acts as a proxy between the Rust-based IC-CDK and JavaScript. **📦 Library:** The IC-CDK for JS/TS is provided by the [@junobuild/functions](https://www.npmjs.com/package/@junobuild/functions) library. To add it to your project: * npm * yarn * pnpm ``` npm i @junobuild/functions ``` ``` yarn add @junobuild/functions ``` ``` pnpm add @junobuild/functions ``` You have to follow the pace of the Juno release to ensure compatibility. Refer to the [maintenance guide](/docs/build/functions/development/typescript.md#maintenance) for instructions. --- ## id Retrieves the Principal ID of the current Satellite. This is useful when you want to use functions such as [setDocStore](/docs/reference/functions/typescript/sdk.md#setdocstore) with the caller set as the administrator. Since the code is running on the backend side (inside the container), the Satellite itself is considered the caller and has admin-level permissions. ``` function id(): Principal; ``` 📦 Import from `@junobuild/functions/ic-cdk` #### Returns: * `Principal`: The Principal ID of the currently executing Satellite. #### Notes: * This function is a JavaScript binding for the Rust function [ic\_cdk::id()](https://docs.rs/ic-cdk/latest/ic_cdk/fn.id.html). --- ## call Makes an asynchronous call to a canister on the Internet Computer. Use this function inside your serverless functions to interact with other canisters. It encodes arguments using Candid, performs the call, and decodes the response based on the expected result type. ``` function call(params: CallParams): Promise; ``` 📦 Import from `@junobuild/functions/ic-cdk` #### Parameters: * `params`: The parameters required to make the canister call. * `canisterId`: The target canister's ID. * `method`: The name of the method to call. Must be at least one character long. * `args` (optional): An array of tuples containing each argument’s Candid type and its corresponding value. * `result` (optional): The expected result type used for decoding the response. #### Returns: * A promise resolving to the decoded result of the call. If the canister returns no value, `undefined` is returned. #### Notes: * This function is a JavaScript binding for the Rust function [ic\_cdk::call()](https://docs.rs/ic-cdk/latest/ic_cdk/api/call/fn.call.html). --- ## time Returns the current timestamp in nanoseconds since the Unix epoch. This is useful when generating timestamps that match the Internet Computer's consensus state. ``` function time(): bigint; ``` 📦 Import from `@junobuild/functions/ic-cdk` #### Returns: * `bigint`: The current timestamp in nanoseconds. #### Notes: * This function is a JavaScript binding for the Rust function [ic\_cdk::time()](https://docs.rs/ic-cdk/latest/ic_cdk/api/fn.time.html). # Node.js The TypeScript runtime used in Juno does not provide full Node.js support. Polyfills are added iteratively to keep the environment stable and predictable. If you require a specific Node.js feature or are blocked by a missing polyfill, please reach out or open an issue. Features are prioritized based on usage and compatibility. --- ## Globals Some global functions are supported but come with important limitations or added details. ### Math.random Generates a pseudo-random number between 0 (inclusive) and 1 (exclusive) is supported. However, the generator is seeded once after upgrade of the Satellite. Use it with caution. **Caution:** Randomness is unsuitable for use cases requiring unpredictable results, like lotteries. ``` const value = Math.random(); ``` --- ### Console Logging is fully supported. Objects are stringified and logs are routed to the IC-CDK `print()` function, making them visible in your Satellite logs including inside the Juno Console UI in development or production. ``` console.log("Hello from the Satellite");console.info("Hello", { type: "info", msg: "Something happened" }); ``` # SDK The following functions are provided to help you work with document and asset data inside your Satellite. They are part of the tools available when writing serverless functions in TypeScript and support common tasks such as interacting with the datastore, storage, and custom hook logic. **📦 Library:** The SDK is provided by the [@junobuild/functions](https://www.npmjs.com/package/@junobuild/functions) library. To add it to your project: * npm * yarn * pnpm ``` npm i @junobuild/functions ``` ``` yarn add @junobuild/functions ``` ``` pnpm add @junobuild/functions ``` You have to follow the pace of the Juno release to ensure compatibility. Refer to the [maintenance guide](/docs/build/functions/development/typescript.md#maintenance) for instructions. --- ## Datastore The following functions can be used to manage documents within the Datastore from your serverless functions. --- ### setDocStore Sets a document in a collection’s of the datastore. Use this to insert or update document data. ``` function setDocStore(params: SetDocStoreParams): void; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing the following fields: * `caller`: The caller initiating the operation (`RawUserId` or `UserId`). * `collection`: The name of the collection where the document will be stored. * `key`: The key identifying the document. * `doc`: The document content including: * `data`: A Uint8Array produced by encodeDocData. * `description` (optional): A short description linked with the document. * `version` (optional if new): An expected version number to prevent overwrite. #### Returns: * `void` #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation is rejected by the Satellite (e.g. due to a failed assertion or validation error). --- ### getDocStore Retrieves a document from the datastore. ``` function getDocStore(params: GetDocStoreParams): OptionDoc; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `caller`: The caller requesting the document (RawUserId or UserId). * `collection`: The collection containing the document. * `key`: The key identifying the document. #### Returns: * `OptionDoc`: The document if found, or undefined. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if retrieval fails. --- ### listDocsStore Lists documents in a collection using optional filters, pagination, and sorting. ``` function listDocsStore(params: ListDocsStoreParams): ListResults; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `caller`: The caller initiating the request (RawUserId or UserId). * `collection`: The collection to list documents from. * `params`: A ListParams object with pagination, filtering, or sorting options. #### Returns: * `ListResults`: Matching documents and pagination metadata. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if listing fails. --- ### countDocsStore Counts documents matching filter criteria. ``` function countDocsStore(params: CountDocsStoreParams): bigint; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `caller`: The caller initiating the operation. * `collection`: The collection to count documents in. * `params`: A ListParams object with filter and pagination options. #### Returns: * `bigint`: Number of documents matching the filter. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation fails. --- ### countCollectionDocsStore Counts the total number of documents in a collection. ``` function countCollectionDocsStore( params: CountCollectionDocsStoreParams): bigint; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `collection`: The collection to count documents in. #### Returns: * `bigint`: The total number of documents. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation fails. --- ### deleteDocStore Deletes a document from the datastore. ``` function deleteDocStore(params: DeleteDocStoreParams): DocContext; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `caller`: The caller initiating the deletion (RawUserId or UserId). * `collection`: The collection where the document is stored. * `key`: The key identifying the document. * `doc`: The document deletion metadata including: * `version`: The expected version of the document. #### Returns: * `DocContext`: Includes the key, collection, and optionally the previous document data. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if validation fails or the document cannot be deleted. --- ### deleteDocsStore Deletes all documents in a specific collection. ``` function deleteDocsStore(params: DeleteDocsStoreParams): void; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `collection`: The collection to delete. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation fails. --- ### deleteFilteredDocsStore Deletes documents matching filter criteria. ``` function deleteFilteredDocsStore( params: DeleteFilteredDocsStoreParams): DocContext[]; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `caller`: The caller initiating the deletion. * `collection`: The collection to target. * `params`: A ListParams object with filter and pagination options. #### Returns: * `DocContext[]`: Context for each deleted document. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation fails. --- ## Storage The following functions can be used to manage assets within the Storage from your serverless functions. --- ### setAssetHandler Sets or updates an asset using identity encoding (no compression). ``` function setAssetHandler(params: SetAssetHandlerParams): void; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: - `key`: An object identifying the asset, including: - `name`: The asset filename (e.g., logo.png). - `full_path`: The asset path (e.g., /images/logo.png). - `collection`: The collection it belongs to. - `owner`: The caller principal. - `token` (optional): An access token if required. - `description` (optional): A short description. - `content`: A Uint8Array representing the raw content of the asset. - `headers`: An array of header fields to store with the asset. #### Returns: * `void` #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation fails. --- ### getAssetStore Retrieves an asset from the Storage. ``` function getAssetStore(params: GetAssetStoreParams): OptionAsset; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `caller`: The identity requesting the asset (`RawUserId` or `UserId`). * `collection`: The collection name. * `full_path`: The asset path. #### Returns: * `OptionAsset`: The asset if found, or `undefined`. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation fails. --- ### listAssetsStore Lists assets in a collection without their content. ``` function listAssetsStore( params: ListAssetsStoreParams): ListResults; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `caller`: The identity making the request. * `collection`: The collection name. * `params`: A `ListParams` object to filter, sort, or paginate. #### Returns: * `ListResults`: Matching assets without content chunks. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation fails. --- ### countCollectionAssetsStore Counts all assets in a collection. ``` function countCollectionAssetsStore( params: CountCollectionAssetsStoreParams): bigint; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `collection`: The collection name. #### Returns: * `bigint`: Number of assets in the collection. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation fails. --- ### countAssetsStore Counts assets in a collection matching filter criteria. ``` function countAssetsStore(params: CountAssetsStoreParams): bigint; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `caller`: The identity making the request. * `collection`: The collection name. * `params`: A `ListParams` object to filter the count. #### Returns: * `bigint`: Number of matching assets. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation fails. --- ### deleteAssetStore Deletes a single asset from the Storage. ``` function deleteAssetStore(params: DeleteAssetStoreParams): OptionAsset; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `caller`: The identity initiating the deletion. * `collection`: The collection name. * `full_path`: The asset path to delete. #### Returns: * `OptionAsset`: The deleted asset, or `undefined`. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation fails. --- ### deleteAssetsStore Deletes all assets in a collection. ``` function deleteAssetsStore(params: DeleteAssetsStoreParams): void; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `collection`: The collection to delete. #### Returns: * `void` #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation fails. --- ### deleteFilteredAssetsStore Deletes assets matching filters in a collection. ``` function deleteFilteredAssetsStore( params: DeleteFilteredAssetsStoreParams): OptionAsset[]; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `caller`: The identity initiating the operation. * `collection`: The collection name. * `params`: A `ListParams` object to define the filters. #### Returns: * `OptionAsset[]`: List of deleted assets. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation fails. --- ### getContentChunksStore Retrieves a specific chunk of an asset. ``` function getContentChunksStore( params: GetContentChunksStoreParams): Blob | undefined; ``` 📦 Import from `@junobuild/functions/sdk` #### Parameters: * `params`: An object containing: * `encoding`: The `AssetEncoding` to retrieve from. * `chunk_index`: The index of the chunk (starting from 0). * `memory`: Either `Heap` or `Stable`. #### Returns: * `Blob | undefined`: The content chunk if found. #### Throws: * `ZodError` if the input schema is invalid. * `Error` if the operation fails. # Utils The following utilities are provided to help you work with document and asset data inside your Satellite. They simplify tasks such as decoding and encoding data, serializing custom types, and interacting with Juno’s core features in a consistent way. **📦 Library:** All utilities on this page are provided by the [@junobuild/functions](https://www.npmjs.com/package/@junobuild/functions) library. To use them, add this to your project: * npm * yarn * pnpm ``` npm i @junobuild/functions ``` ``` yarn add @junobuild/functions ``` ``` pnpm add @junobuild/functions ``` --- ## decodeDocData Decodes the raw data of a document into a typed object. Use this inside hooks or assertions to retrieve the original contents of a document. ``` function decodeDocData(data: Uint8Array): T; ``` 📦 Import from `@junobuild/functions/utils` #### Parameters: * `data`: A Uint8Array containing the raw binary document data. #### Returns: * A deserialized object of type `T`. --- ## encodeDocData Encodes an object into a raw binary format for storing document data. Use this when creating or modifying a document inside a hook or assertion. ``` function encodeDocData(data: T): Uint8Array; ``` 📦 Import from `@junobuild/functions/utils` #### Parameters: * `data`: A JavaScript object to serialize. #### Returns: * A `Uint8Array` containing the encoded binary data.