diff --git a/docs/configuration/secrets/index.html b/docs/configuration/secrets/index.html index d633ff5..24abeb5 100644 --- a/docs/configuration/secrets/index.html +++ b/docs/configuration/secrets/index.html @@ -1,5 +1,5 @@ Secrets Management – Internal Tools Deployment Platform | Clace -
  • Managing Apps
  • Secrets Management

    Secrets Management

    Clace supports secret management when working with apps. Secrets can be passed to containerized apps through the environment params. Secrets can also be passed to any plugin as argument. For OAuth config, the client secrets can be configured as secret in the config file.

    Supported Providers

    Clace currently supports AWS Secrets Manager (ASM) and HashiCorp Vault as providers for secrets management. Secrets can also be read from the environment of the Clace server, which can be used in development and testing.

    AWS Secrets Manager

    To enable ASM, add one or more entries in the clace.toml config. The config name should be asm or should start with asm_. For example

    clace.toml
    [secret.asm]
    @@ -129,7 +129,15 @@
     

    Secrets can be accessed using the syntax {{secret "PROVIDER_NAME" "KEY_NAME"}}. The three contexts in which secrets can be accessed are:

    Secrets are always resolved late. The Starlark code does not get access to the plain text secrets. The secret lookup happens when the call to the plugin API is done. In case of params, the lookup happens when the param is passed to the container.

    For git_auth config, an example secret usage is

    clace.toml
    [auth.google_prod]
     key = "mykey.apps.googleusercontent.com"
     secret = '{{secret "PROVIDER_NAME" "GOOGLE_OAUTH_SECRET"}}'
    -hosted_domain = "example.com"

    Multiple Keys +hosted_domain = "example.com"

    Plugin Access to Secrets +

    For secrets which are passed to plugins, through app params or plugin arguments, the plugin needs to be authorized to access the secret. The permissions for each plugin are defined in the app definition. For example:

    app.star
    app = ace.app("test",
    +              routes = [ace.api("/", type="TEXT")],
    +              permissions = [
    +                ace.permission("exec.in", "run", ["ls"], secrets=[["c1", "c2"], ["TESTENV"]]),
    +              ]
    +             )

    The secrets accessible are specified as a list of list of strings. In this case, the {{secret "PROVIDER_NAME" "c1" "c2"}} and {{secret "PROVIDER_NAME" "TESTENV"}} calls are allowed. Additional keys are also permitted.

    Multiple Keys

    If the KEY_NAME is a single string, it is passed as is to the provider. If multiple keys are specified, they are concatenated and passed to the provider. For example, {{secret "env" "ABC" "DEF"}} will get converted to a env lookup for ABC_DEF. The delimiter used depends on the provider. The defaults are:

    • ASM and Vault : /
    • Env : _
    • Properties: .

    The formatter used to concatenate the keys can be customized by setting the keys_printf property. For example,

    clace.toml
    [secret.prop]
     file_name = "/etc/mykeys.properties"
    -keys_printf = "%s-%s.%s"

    combines {{secret "prop" "ABC" "DEF" "XYZ"}} as ABC-DEF.XYZ. This allows the app to work with multiple secret providers without requiring code changes in the app.

    \ No newline at end of file +keys_printf = "%s-%s.%s"

    combines {{secret "prop" "ABC" "DEF" "XYZ"}} as ABC-DEF.XYZ. This allows the app to work with multiple secret providers without requiring code changes in the app.

    Default Provider +

    If the provider name is passed as default or set to empty, a default provider is used. The default provider can be configured in the clace.toml as

    clace.toml
    [app_config]
    +security.default_secrets_provider = "env"

    The env provider is used by default if it is enabled in the config. The default can be changed per app by setting

    clace app update-metadata conf --promote 'security.default_secrets_provider="prop_myfile"' /myapp
    App Authentication
    \ No newline at end of file diff --git a/en.search-data.json b/en.search-data.json index 9d2ad0e..9726234 100644 --- a/en.search-data.json +++ b/en.search-data.json @@ -1 +1 @@ -{"/about/":{"data":{"":"","#":"What is Clace? Clace is an Apache-2.0 licensed project building a web app development and deployment platform for internal tools. Clace allows easy and secure hosting of multiple web apps, in any language/framework, on a single machine. Clace is cross-platform (Linux/Windows/OSX) and provides a GitOps workflow for managing web apps.\nProject Goals The goal of this project is to make it easy for individuals and teams to develop and manage lightweight full stack applications in a secure manner. Clace aims to make it easy to install and manage secure self-hosted web applications with minimal operational overhead. Easy integrations to enable SSO/SAML based authentication and authorization controls, audit logs and integration with secrets manager for managing credentials are goals.\nA single developer should be able to manage the full application lifecycle: frontend and backend development and production deployment. Deployments should support a GitOps approach, with automatic preview environment to verify changes before making them live. It should be easy, for the original developer or a new one, to make application code changes and deploy - after six months or after six years.\nWhat’s with the name The name Clace is a play on Command Line Ace, since building an UI for command line applications was an initial target use-case. The name is pronounced like Place, with a C.\nHow is Clace implemented? Single binary web application server (in golang), with a set of plugins built in (also in golang) which allow access to external endpoints. The server is statically configured using a TOML file. Applications are configured using Starlark, which is a subset of Python. Python is an ideal glue language, Starlark is used to configure the application backend logic Multiple applications can be dynamically installed, an embedded SQLite database is used to store application metadata (Postgres support is in the roadmap). For applications using the container plugin, Clace works with Docker or Podman using CLI to build and run the containers. Path based routing, each app identified by a unique path. Also, domain based routing, which allows multiple domains to point to the same Clace instance, with path based routing being done independently for each domain. Automatic TLS certificate management for each domain to simplify deployments. A sandboxing layer is implemented at the Starlark(python) to Golang boundary, allowing the implementation of security and access control policies. Go code is trusted, Starlark code is untrusted. For Starlark based apps, the application UI is implemented using Go HTML templates, with HTMX for interactivity. Go templates support context aware templating which prevents encoding related security issues. They also work well with the HTML fragments required for HTMX. No need to install any additional components like Python or NodeJS/NPM on the host machine. Integration with tailwindcss-cli is supported. esbuild (using the esbuild go library) is supported out of the box for importing ESM modules. Current Status The current status as of Aug 2024 is:\nClient and server (in a single binary) for service management and configuration. Support for application development with Starlark based configuration. Container management support with Docker and Podman Auto-idling of containers to reduce resource usage Go HTML template loading and caching for request processing. HTTP plugin for communicating with REST endpoints. Exec plugin for running system commands. Built in admin account for local development. Auto-sync (file system watcher) and Auto-reload using SSE (automatic UI refresh) for speeding up the application development cycle. Admin functionality using unix domain sockets for security. Application sandboxing checks to ensure only audited operations are allowed. Staged deployment support, preview app creations support. App data persistence to sqlite with managed tables. Who is behind this project? The project was started by Ajay Kidave. Ajay’s background has been in database systems and enterprise integration tools. Clace was started to find ways to reduce the development and operational complexity in tooling for internal applications.\nHow to stay in touch? Star the repo at github.com/claceio/clace Email at contact@clace.io Follow on Twitter Subscribe to the blog RSS feed Connect on Discord "},"title":"About"},"/blog/appserver/":{"data":{"":" 📎 Clace is an open source platform for developing internal tools and deploying them across a team. Clace can auto-generate an UI for backend actions. Clace is the first internal tools platform built for Hypermedia Driven Applications and first internal tools platform on the CNCF landscape. Application Servers can make application deployment easy. AppServers do not support all the features of a PaaS solution but that comes with the benefit of zero config deployments. Especially for internal tools, AppServers are a great alternative to building a deployment solution on top of Kubernetes. Clace is the first AppServer built for use with containers.","appserver-features-of-clace#AppServer Features of Clace":"Clace is built as a platform for teams to deploy internal tools. As part of that, Clace implements an AppServer to deploy containerized apps. The goal is to make it easy for teams to deploy and manage Streamlit/Gradio type apps for internal users. Clace provides blue-green staged deployment, GitOps, OAuth access control, secrets management etc for the apps.\nWith Clace, any Containerized app (having a Dockerfile) can be installed using a command like\nclace app create --spec container --approve github.com/\u003cUSERID\u003e/\u003cREPO\u003e /myapp The app will be available at the /myapp url. For many frameworks, zero config is required. Not even a Dockerfile is required. For example\nclace app create --spec python-streamlit --branch master --approve github.com/streamlit/streamlit-example /streamlit_app deploys a Streamlit based app.\nEach app has a dedicated url, domain based or path based. Clace ensures that no other app can conflict with that path. Clace can currently scale between zero and one instance of the container. More than one is not supported since Clace runs on a single machine (multi-node support is planned). Clace has a CLI interface currently, a declarative interface based on the CLI is planned.\nFor use cases where teams are deploying internal tools, Clace can provide a much simpler solution as against using a general purpose PaaS solution.\nUpdate (Jan 2025): Clace is now listed in the CNCF Landscape. ","appservers-miss-the-cloud-native-train#AppServers Miss the Cloud-Native Train":"Since the initial release of Docker in 2013, containers have become very popular for application deployment. Containers have the advantage of encapsulating the application runtime and dependencies in an easily deployable image. Being able to set resource limits on CPU/memory/disk usage enables isolation across applications.\nNo application server currently supports running apps within containers. App servers are missing from the crowded cloud native landscape (possibly the only infrastructure software component missing there).","cloud-native-appserver-features#Cloud-Native AppServer Features":"A cloud-native application server would include the following features:\nContainer-Based: Uses containers for application deployment, with isolation across apps. Easy Config: Provide zero config or simple config approach GitOps : App deployment driven by source code changes. Elastic Scalability: Scale down to zero, scale up as required based on load. Declarative Configuration: All configuration is applied declaratively as opposed to being imperative. The AppServer is not replacing the language specific services. For example, with Python, Gunicorn/Uvicorn would provide the WSGI/ASGI functionality within the container.","multi-language-appservers#Multi-Language AppServers":"Some of the prominent multi-language app servers are:\nNGINX Unit: Nginx Unit is a different application from the regular Nginx web server. Unit is configured through JSON based APIs. Packaging apps to work with Unit is not straightforward. For example, see Unit Java sample. uWSGI: uWSGI is a “full stack for building hosting services”. It supports many powerful features for process management. Combining all its features and configuring them correctly is non-trivial. Many languages are supported but outside of interpreted languages, it is not easy to configure. For example, see uWSGI Java config. The uWSGI project is currently in maintenance mode. Phusion Passenger: Phusion Passenger primarily supports Ruby, Python and Javascript. Passenger 6 added support for generic apps. This requires changing the app to pass the port to use on its command line. For example, Passenger Java. These projects either run the app in-process or use a process model to run each application separately. The in-process model has issues with ensuring stability of apps when another app misbehaves. Even with the multi-process model, complete isolation across apps is not supported.","paas-vs-appservers#PaaS vs AppServers":"Most of the recent innovation in the container orchestration space have focussed on providing support for hosting the complete software stack. This includes deploying stateless applications, stateful databases, object stores and any other type of application. The goal has been to build Platform-As-A-Service solutions (PaaS). Kubernetes is built as a platform for building platforms. Even beyond Kubernetes, most container deployment platforms focus on trying to provide a complete PaaS solution. Since the scope of applicable use cases is large, even the simplest use case requires complex configuration with a PaaS solution.\nAppServers by definition are simpler. They support deploying stateless applications. Given the source code for a service, an AppServer can run the service and give an HTTP endpoint to access the service. AppServer can provide a standardized deployment approach, irrespective of the language/framework. AppServers do not support deploying databases or queues or object stores.\nMany teams choose managed services (like AWS RDS or MSK) for data persistence. If the stateful applications are externally managed, then AppServers can be used for deploying the stateless applications. This avoids the complexity of having to maintain PaaS configurations.","what-is-an-application-server#What is an Application Server":"An Application Server is a service that makes it easy to deploy applications and provides common features required by most apps. This includes implementing connection handling (HTTP request routing), deployment and versioning, logging, monitoring and authentication features.\nMany application servers are programming language specific. In the Java and .Net ecosystems, class loader level isolation is used to implement app servers. The request processing model of PHP also makes app servers suitable for serving PHP apps. For interpreted languages like Python and Ruby, there are some app servers which provide cross language support. App server support is limited for compiled languages. This article will focus on app servers which support multiple languages, since those are more widely usable."},"title":"Missed Connections: AppServers in the Containerized Landscape"},"/blog/errors/":{"data":{"":" 📎 Clace is an open source platform for developing internal tools and deploying them across a team. Clace can auto-generate an UI for backend actions. Clace is the first internal tools platform built for Hypermedia Driven Applications and first internal tools platform on the CNCF landscape. ","background#Background":"Most programming languages handle error conditions either by supporting error values or by means of exceptions. Error return value support varies, with some statically typed languages enforcing that the error conditions be handled. With exceptions, the invoker can either catch and handle it or it automatically gets thrown up the stack. The purpose of error handling is to allow an invoker to either handle the issue or allow the invoker to return the error to the caller.\nExceptions have the advantage of the error handling being automatic. Error values have the advantage of requiring more explicit error handling, at the cost of verbosity.","can-this-be-a-generic-solution#Can this be a generic solution?":"The Clace runtime provides all the APIs used by Clace apps by means of plugin calls. This solution can be applied when\nAll code that can cause errors are provided through a standard API interface Thread locals are feasible for tracking errors There is a standard error handling function which does something useful (could be user defined) The error check happens at the API boundary (Starlark to Go in this case). If there is code which does excessive CPU usage or memory allocation, that code will run before the automatic error check kicks in. That should not be an issue in practice for glue code as used by Clace.\nThis error handling solution is limited in scope to use cases where glue scripts are being written which make lots of API calls. This provides a shell errexit type feature for regular code. This does not support error handling that needs to happen within user defined code, like one function which returns an error to be handled by another function.\nHandling resource leaks is another concern. For Clace, since all resources (transactions, result sets etc) are created through the plugin API, they are automatically closed when an error occurs.","error-handling-for-glue-code#Error handling for Glue code":"For code which is gluing together multiple API’s, error handling can be tedious. Some languages have specific support for this. The most famous example is the errexit setting set -e in shell scripts. This will automatically check each command for error return status and fail the script if an error occurs.","how-does-this-work#How does this work?":"The automatic error handling feature of Clace keeps track of every plugin call’s status. If the plugin call fails, the Clace runtime makes sure that return value cannot be accessed, unless an explicit error check was done. If no explicit check is done, the Clace runtime will fail the API, calling the user defined error handler or a generic error handler if none is defined. So for the code\ndef insert(req): store.begin() book = doc.bookmark(\"abc\", []) ret = store.insert(table.bookmark, book) print(ret.value) store.commit() If begin() fails, the call to insert() will fail since the previous error was not handled (begin’s error message is raised). If insert() fails, the value access will fail, so the print will not run If commit() fails, the Clace runtime will first check whether the last plugin failed before handling the API response. Thread locals are used to track errors across plugin API calls. This works since an API handler Starlark function is single threaded. When begin() fails, it sets a thread local. If the error is explicitly checked, like\nret = store.begin() if ret.error: pass print(ret.value) then the thread local state is cleared. So if the code is doing explicit error checks, the automatic error handling is disabled.","is-there-a-third-option#Is there a third option?":"Clace is built to be a platform for building internal tools. Clace is built in Go and uses Starlark for app configuration and also for API business logic. Starlark does not support exceptions and does not support multi value returns. This makes error handling difficult. The solution implemented for Clace is an API boundary error checker with the following properties:\nAutomatic error handling, no explicit error checks required for every API call Easy way to do explicit error checks when errors are expected This gives the best of both worlds. All error conditions are automatically checked like exceptions. When explicit error checks are required, they are easy to do like error values.","trade-offs#Trade-Offs":"The two main trade-offs we are handling are:\nExceptions are automatically checked, but they result in error handling being done away from the code which actually failed. Error values need to be checked explicitly, but the error handling code is local to where the error occurred. In most languages, it is easy to miss checking for errors (Rust being one exception with the Result type) The ideal scenario in terms of code verbosity is that error handling should be automatic. The ideal scenario in terms of proper error handling is that the explicit error checks should be easy for the invoker, else the automatic error handling kicks in."},"title":"Errors and Exceptions: Is there a third option?"},"/blog/go-composition/":{"data":{"":" 📎 Clace is an open source platform for developing internal tools and deploying them across a team. Clace can auto-generate an UI for backend actions. Clace is the first internal tools platform built for Hypermedia Driven Applications and first internal tools platform on the CNCF landscape. ","background#Background":"I recently encountered an issue where Server-Sent Events (SSE) stopped working in Clace. SSE are used for live reload functionality in Clace. The problem turned out to be a recent change in Clace which added support for tracking HTTP response status code. This was implemented by implementing a composition over the http.ResponseWriter to keep track of the status code. This composition broke the SSE functionality.","composition-breaks-implicit-interfaces#Composition breaks Implicit Interfaces":"The reason for the issue encountered is an implicit interface http.Flusher implemented by most implementations of http.ResponseWriter. Adding a composition over http.ResponseWriter causes this implicit interface to no longer be implemented.\ntype CustomWriter struct { http.ResponseWriter statusCode int } Here, CustomWriter no longer implements http.Flusher, even if the underlying http.ResponseWriter implementation did. SSE was supported for flushable writers only, so adding the composition broke SSE. The fix is to have the composing struct explicitly implement http.Flusher. See go playground to see an example.","composition-over-inheritance#Composition over Inheritance":"Go supports embedding, which can be used to implement Composition (has-a relationship) rather than inheritance (is-a relationship). Composition has some benefits over inheritance. Embedding in Go allows the use of composition without requiring forwarding methods.","fixing-the-issue#Fixing the issue":" func (cw *CustomWriter) Flush() { if flusher, ok := cw.ResponseWriter.(http.Flusher); ok { flusher.Flush() } } Adding a Flush function is a fix, but an issue with this is that if the caller had support for non-flushable writers, that behavior is lost. A better fix is to have two implementation, one flushable and another non flushable. The appropriate one should be used based on whether the underlying writer implements flusher. This way, the original behavior is not changed by adding the composition.\ntype FlushableWriter interface { http.ResponseWriter http.Flusher } type FlushableCustomWriter struct { FlushableWriter statusCode int } type CustomWriter struct { http.ResponseWriter statusCode int } Some HTTP routers like Chi have middleware which implement the required implicit interfaces.","how-to-avoid-this-issue#How to avoid this issue":"The Go type system does not have a way to catch such issues are compile time. At runtime, the issue can show up as a performance degradation (if the implicit interface is used as an performance optimization) or as a unexpected behavior (if custom behavior is implemented using the implicit interface).\nIt would have helped if the documentation for http.ResponseWriter had mentioned the http.Flusher interface and when it is used. This is feasible when the types are in the same package.\nThe takeaway is that if using composition over types which could have implicit interfaces, it is important to look at whether any of those implicit interfaces have to be explicitly implemented by the composing type.\n💬 Discussion thread on Reddit ","implicit-interfaces#Implicit Interfaces":"Interfaces in Go are implemented implicitly. This is a powerful feature, allowing interfaces to be added when required later. Interfaces can even be created for types in different packages. This allows clients to control how types are used rather than depending on how the types were originally defined.","stdlib-implicit-interfaces#Stdlib Implicit Interfaces":"The Go stdlib uses implicit interfaces to implement optimizations (like io.WriterTo and io.ReaderFrom) and custom behaviors (like fmt.Stringer). Other similar interfaces are http.Hijacker, http.Pusher, and io.Closer."},"title":"Go Composition does not compose well with Implicit Interfaces"},"/blog/intro/":{"data":{"clace-platform-for-managing-internal-tools#Clace: Platform for Managing Internal Tools":" 📎 Clace is an open source platform for developing internal tools and deploying them across a team. Clace can auto-generate an UI for backend actions. Clace is the first internal tools platform built for Hypermedia Driven Applications and first internal tools platform on the CNCF landscape. Clace: Platform for Managing Internal ToolsClace is an open-source platform to enable easy development and deployment of web applications for internal tools. The goals for the Clace project are:\nEnable development and deployment of secure internal web applications. Simplify ongoing maintenance of such apps by removing build and dependency related issues. Updating an app after six months or six years should just work. Provide portable and flexible deployment options, easy to use on developer machines and also on a shared server across teams. ","current-status#Current Status":"Clace is in a beta state currently. Custom application support is functional. Support for loading plugins dynamically is in progress. You can try out Clace (on OSX, Linux or Windows with WSL) by doing:\ncurl -L https://clace.io/install.sh | sh source $HOME/clhome/bin/clace.env clace server start \u0026\u0026 sleep 2 clace app create --approve github.com/claceio/apps/system/disk_usage /disk_usage The app should be available at https://127.0.0.1:25223/disk_usage after allowing the self-signed certificate. admin is the username, use the password printed by the install script. See installation for details.","follow-along#Follow Along":"You can keep in touch by these means:\nStar the repo at github.com/claceio/clace Sign up for Email updates Follow on Twitter Subscribe to the blog RSS feed Follow ClaceIO on LinkedIn Connect on Discord Use discussions feature in Github or raise issues to provide feedback.","how-does-it-work#How does it work?":"Clace applications are configured in Starlark, which uses a subset of Python syntax. The API routes are defined to be Hypermedia first, using HTML templates to drive the UI interactions. Templates are written using Go HTML templates. HTMX is used for server interactions. The backend code runs in a security sandbox and every access to plugins need to be explicitly permitted. Application updates can be done with no build step required. Clace integrates with TailwindCSS/DaisyUI for styling and has esbuild built-in for ESM support.","security#Security":"The Starlark backend code for Clace runs in a sandbox, all external interactions need to go through plugins. The Clace platform implements a security sandbox at the plugin boundary. Applications define what operations they need to be able to perform. The platform enforces these permissions at runtime.\nThis security model enables the following:\nUsers can download applications and run on their machine, without worrying about what operations the app can do on their system outside the audited permissions. Operations teams can install and approve applications. Further application updates can be handled by the development team, without requiring the operational admins to verify the updated code. As long as the application works within the originally defined permission boundary, application updates will continue to work. Application developers can use LLM powered automated code generation tools without worrying about the side-effects of the code. If the generated code tries to perform any operation not previously approved, it will fail. The sandbox will ensure that the apps can do only authorized operations. This makes Clace an ideal target for LLM (like GPT) generated applications. The Clace platform will add the authentication/authorization, gitops based deployment and operational monitoring features on top of the generated app.","use-cases#Use-cases":"Clace is built to solve two different types of use-cases:\nCustom applications: With fully customizable UI, this would be similar to solutions like Retool. A low-code approach is used, with a focus on Hypermedia driven applications. Actions: This would be similar to solutions like Rundeck. A way to automate internal applications, with a form based interface, with support for triggered and scheduled execution. One of the aims of Clace is to make it possible for everyone, especially backend engineers, to develop and use simple web interfaces. For use-cases where a CLI was developed previously, a Clace based UI could be built. The backend service could invoke the CLI command or directly call the internal API which need to be exposed. Development and use of simple web interfaces for all types of use-cases should be made easier with Clace."},"title":"Introducing Clace"},"/blog/sqlite/":{"data":{"":" 📎 Clace is an open source platform for developing internal tools and deploying them across a team. Clace can auto-generate an UI for backend actions. Clace is the first internal tools platform built for Hypermedia Driven Applications and first internal tools platform on the CNCF landscape. ","background#Background":"Clace is built to serve web applications, primarily for internal tools. Clace provides functionality usually handled separately by a web server and an application server. When the development of Clace was started last year, one of the first decisions was how to store the application data (files) and metadata. The app metadata obviously made sense to store in a database, since apps are created dynamically. The app data (static files, app code, config files etc) is usually stored on the file system by most web servers.","benefits-of-using-sqlite#Benefits of using SQLite":"The decision to use SQLite for file storage has provided lots of additional benefits (some unanticipated).\nTransactional Updates : This is the main benefit. Updating multiple files can be done in one transaction. Isolation ensures that there are no broken webapps during the update.\nDeployment Rollbacks: Another of the transactional benefits is the ability to roll back deployment in case of errors. If multiple apps are being updated, all of them can be rolled back in one go. Rolling back a database transaction is much easier than cleaning up files on the file system.\nFile De-duplication Across Versions: Clace automatically versions all updates. This can lead to lots of duplicate files. The file data is stored in a table with the schema\nCREATE TABLE files (sha text, compression_type text, content blob, create_time datetime, PRIMARY KEY(sha)); The uncompressed content SHA256 hash is used as the primary key to store the file data. This means that even if multiple versions of an app have the same file, the file contents are stored only once.\nDe-duplication Across Apps : Each production app in Clace has an staging app. Apps can have multiple previews apps. This can lead to lots of duplication of files. Using the database helps avoid all the duplication. Even across apps, there are files which have the same contents. Files are de-duplicated across apps also.\nEasy Backups: Using SQLite means that backups are easy. The state of the whole system, metadata and files can be backed up easily using SQLite backup tools like Litestream.\nContent Hashing: For content caching on the browser, web servers return a ETag header. Using the database for files makes it easy to save the content SHA once during file upload without having to recompute it later.\nCompression: The file contents are saved Brotli compressed in the SQLite table. The database approach has the advantage that contents can be saved in multiple formats easily. GZip compressed data and uncompressed data can be added by just adding a column in the files table.","multi-node-support#Multi-Node Support":"Clace currently runs on a single node. When multi-node support is added later, the plan is to use a shared Postgres database instead of using local SQLite for metadata and file data storage. This will come with latency issues. The plan is to use a local SQLite database as a file cache to avoid latency while accessing Postgres.","performance#Performance":"For Clace, the SQLite database approach provides great performance. There is no equivalent implementation using the file system to compare against, so a direct benchmark test is not done. Based on benchmarking done by the SQLite team, SQLite can have better performance than direct file system use for some workloads.","using-sqlite-for-serving-files#Using SQLite for serving files":"For Clace, the decision was made to use SQLite for app files storage instead of using the file system. The reasoning was mainly to be able to do atomic version changes. When updating an app, since there could be lots of files being updated, using a database would allow all changes to be done atomically in a transaction. This would prevent broken web pages from being served during a version change.\nClace uploads all files to the SQLite database during app creation and updates. Files are uploaded from GitHub or from local disk. Only for development mode, the local file system is used.","why-this-approach-is-not-more-common#Why this approach is not more common?":"One of the reasons most web servers use the file system is convenience. File updates can be done using any file system tool: rsync, tar etc work for copying files over. The other reason is probably historical: file systems are what were used before there were good in-process relational databases available. Using a database means some kind of API interface is required for uploading files, which is not always feasible.\n💬 Discussion thread on Hacker News "},"title":"Using SQLite as Storage for Web Server Static Content"},"/blog/webserver/":{"data":{"":" 📎 Clace is an open source platform for developing internal tools and deploying them across a team. Clace can auto-generate an UI for backend actions. Clace is the first internal tools platform built for Hypermedia Driven Applications and first internal tools platform on the CNCF landscape. ","app-level-isolation#App-Level Isolation":"The goal of app-level isolation is for developers to confidently update web server routing rules, being sure that a broken update will not impact other apps.\nTo enforce isolation across apps, web servers first need a concept of an app. There needs to be a way to say which rules are for which app. Each update has to be scoped to be for a particular app. If there are rules which conflict with another app, those rules need to be rejected.","background#Background":"Clace is a platform for developing and deploying internal tools. Clace provides functionality usually handled separately by a web server and an application server. The web server part of Clace is built such that there is complete isolation between app-level routing rules. Creating a new app or updating an existing app cannot break other apps. This post goes into details about how this is done and why it is useful.","clace-approach#Clace Approach":"Clace has the concept of an app where each app is installed for a path within a domain. If app A is installed at appA.example.com:/, then that domain is completely dedicated for that app. An app installed at appB.example.com:/test owns the /test/* namespace for domain appB.example.com, no other app can use that namespace.\nThis approach has the benefit that each app gets a dedicated namespace. Within that namespace, the app can do whatever it wants, without interfering with other apps. At app installation time, a check is done whether the domain path being requested is available. If some other app is using that path, the new app installation is rejected. A SQLite database is used to store app metadata, so API driven app updates do not conflict with each other.\nWithin the app, rules are defined using Starlark. This avoids the need to learn a new DSL. The routing config is read at app initialization only and the actual handler logic can be expressed alongside the routing rules. For containerized apps, the routing rule specifies the revery proxy url. Reloading an app updates its config, without impacting other apps.","conclusion#Conclusion":"By enforcing app-level isolation in routing rules, Clace allows each app to manage its own domain and path namespace without risking conflicts or breakages. This approach encourages developers to utilize efficient web server–level routing features, confident that changes in one app won’t disrupt others. Some webserver routing use cases which are more complex cannot use this approach, but this is useful for app deployment scenarios.","updating-routing-rules#Updating Routing Rules":"Most web servers use a config file for specifying the rules for request routing. The config file is generally a DSL which specifies the API rules. Some servers like Caddy support JSON input. Encapsulating or grouping together rules related to an app is supported, but this encapsulation is not enforced. For updating the rules, the approach is to update the file on disk and send a reload request to the web server. Even for API based updates, the approach is usually to send the whole config contents, partial updates are not supported.\nThe issue with this approach is that if there are multiple apps, updating the rules for one app can break other apps. There is no enforcement of isolation across apps. This results in app developers trying to avoid updating the web server, doing more in the application server when it would have been more efficiently done in the web server.","web-server-overview#Web Server Overview":"A Web Server is software that accepts HTTP/HTTPS requests and routes them appropriately. Web Servers provide features like URL rewrites, reverse proxying, header manipulation, static file serving, and WebSocket connection handling. Web Servers can accept connections on multiple ports. A common pattern is that all requests are received on port 80 (for HTTP) and 443 (for HTTPS) and routing is done based on request domain (from the Host HTTP header) and path (from the url). Apache, Nginx, and Caddy are popular web servers."},"title":"App-Level Isolation in Web Server Config"},"/docs/":{"data":{"":"Clace is an open-source Apache-2.0 licensed project for easy development and deployment of self-hosted web apps. Clace provides a web application server focussed on securely running multiple applications on a single installation.\nQuick StartQuick overview of using Clace InstallationDetails about installing Clace server ConfigurationClace server configuration details Managing appsWorking with Clace apps Developing appsDeveloping Clace apps PluginsDetails about Clace plugins "},"title":"Docs"},"/docs/actions/":{"data":{"":"Actions allow apps to expose an autogenerated UI for simple backend actions. For use cases where an existing CLI application or API needs to be exposed as a web app, actions provide an easy solution. An app can have one or more actions defined. Each action has to be given a unique path, which does not conflict with any other route defined for the app. See weather app code:demo for an example of using Actions.","action-definition#Action Definition":"An action is defined using the ace.action struct. The fields in this structure are:\nProperty Optional Type Default Notes name false string The action name path false string The path to use within app path run false function The function to run on execution suggest true function none The function to run on suggest description true string none The description for the action hidden true list strings none The params which should be hidden in the UI for this Action show_validate true boolean False Whether to show an Validate option for this action The name and description are shown in the app UI. The app params are displayed in a form. BOOLEAN types are checkboxes, others are text boxes.\nWhen the form is submitted, the run function is called. The params are passed as an args argument. The response as returned by the handler is shown on the UI.\n⚠️ In the action handler function, use args argument to get the values from the form. Referencing params will give the default values for the parameters, not the actual values passed in. The hidden property can be used to hide params for specific Actions. Set it to the list of params to hide, for example hidden=[\"param1\"].","action-result#Action Result":"The handler returns an ace.result struct. The fields in this structure are:\nProperty Optional Type Default Notes status true string The action status message values true list [] The actions output, list of strings or list of dicts report true string ace.AUTO The type of report to generate. Default is ace.AUTO, where it is selected based on response type. Other options are ace.JSON, ace.TEXT, ace.TABLE, ace.DOWNLOAD and ace.IMAGE. Any other value is a custom template name. param_errors true dict {} The validation errors to report for each param. The key is the param name, the value is the error message ","custom-templates#Custom Templates":"If the report type is set to any value other than ace.AUTO/TEXT/JSON/TABLE, that is treated as a custom template to use. The template should be defined in a *.go.html file. Either the file name can be used or a template/block name can be used. See template for details.\nFor styling, Clace uses DaisyUI by default, so default styles are reset. The custom template can use inline styles or it can use TailwindCSS/DaisyUI. For DaisyUI, the app has to be run in dev mode first for the style.css to be generated. See styling for details.\nSee dictionary code:demo for an actions example app which shows different type of reports.","display-types#Display Types":"For string type params, the display_type property can be set to FILE, PASSWORD or TEXTAREA. If no value is set, the field shows as a text input box. FILE param shows as a file upload input. PASSWORD shows as a password input. TEXTAREA shows as a text area.","file-handling#File Handling":"For FILE display type, the Action app user can upload a file. The file is uploaded to a temp file on the server and the file name is available through the args.param_name. The file can be process as required from disk. Multiple FILE type params are supported, each param can upload one file only. The temp files are deleted at the end of the handler function execution.\nTo return file as output for the Action, using the fs.serve_tmp_file API. This makes a file on disk available through an API.\nSee number_lines app code:demo for an example of using this API. Use report=ace.DOWNLOAD property in the ace.result to generate a file download link.\nFiles from the system temp directory and from /tmp are accessible by default for serve_tmp_file API. The file is deleted from disk by default after the first download. This can be configured at the system level using\nclace.toml[app_config] fs.file_access = [\"$TEMPDIR\", \"/tmp\"] To set this at the app level, run\nclace app update-metadata conf --promote fs.file_access='[\"/var/tmp\", \"$TEMPDIR\", \"/tmp\"]' /myapp ","multiple-actions#Multiple Actions":"Multiple actions can be defined for an app. Each action should have a dedicated path. If there are multiple actions, a switcher dropdown is automatically added for the app. The order of entries in the dropdown is the same order as defined in the app.\nSee weather app code:demo for an example of using multiple actions in one app.","param-value-selector#Param Value Selector":"For some params, it is useful to be able to provide a list of values from which the user can choose. The way this is supported is by using an options param. If param1 is a param which should show up as a selector, then define another param with the name options-param1, of type LIST. Set a default value for options-param1 with the values to show in the selector dropdown. For example\nparams.starparam(\"param1\", description=\"The param1 description\", default=\"option1\") param(\"options-param1\", type=LIST, description=\"Options for param1\", default=[\"option1\", \"option2\"]) In the UI, options-param1 is not displayed. param1 is show with a selector, having option1 and option2 as options. See dictionary for an app which uses this.\nThis approach is used for flexibility, instead of directly allowing the options to be configured for the param. The options param approach has the flexibility that when an app is installed, the options can be configured for the installation. This avoids having to maintain different copies of the app code. For example:\nclace app create --approve --param options-param1='[\"option1\", \"option2\", \"options3\"]' /mycode /myapp adds a new options3 option.","report-types#Report Types":"The response values can be a list of string or a list of dicts. The report is generated automatically by default. For list of strings, the report is a TEXT report. For list of dicts, the report can be either\nTABLE - selected if all dict values for the first row are simple types JSON - selected if any of the values for the first row is a complex type (like dict or list) For TABLE report, the fields from the first row are used as columns. Extra fields in subsequent rows are ignored. For JSON report, a JSON tree representation of each row is shown. The report type can be set to specific type instead of using AUTO.","sample-action#Sample Action":"First, define the parameters to be exposed in the form UI. Create a params.star file with the params. For example,\nparams.starparam(\"dir\", description=\"The directory to list files from\", default=\"/tmp\") param(\"detail\", type=BOOLEAN, description=\"Whether to show file details\", default=True) This app defines a run handler which runs ls on the specified directory. The output text is returned.\napp.starload (\"exec.in\", \"exec\") def run(dry_run, args): if args.dir == \".\" or args.dir.startswith(\"./\") or args.dir == \"..\" or args.dir.startswith(\"../\"): return ace.result(\"Validation failed\", param_errors={\"dir\": \"relative paths not supported\"}) cmd_args = [\"-Lla\" if args.detail else \"-La\", args.dir] out = exec.run(\"ls\", cmd_args) if out.error: return ace.result(out.error) return ace.result(\"File listing for \" + args.dir, out.value) app = ace.app(\"List Files\", actions=[ace.action(\"List Files\", \"/\", run, description=\"Show the ls -a output for specified directory\")], permissions=[ ace.permission(\"exec.in\", \"run\", [\"ls\"]), ], ) The app, when accessed will look as shown below, with the ls command output displayed:","suggest-handler#Suggest Handler":"If a suggest handler is defined for an action, then a Suggest button shows up in the UI. Suggest allows property values to be populated dynamically. For example, if the app has three params A, B and C, and all are empty initially. The first suggest can do return {\"A\": [\"avalue1\", \"avalue2\", \"avalue3\"]}. This will populate the A param with a dropdown. A subsequent suggest call can populate the value for B, with a list of options or with an actual value. The suggest handler is optional. A sample suggest handler is\napp.stardef suggest(args): if not args.A: alist = [] res = store.select(table.adata, {}) for aval in res.value: alist.append(aval.name) return {\"A\": alist} else: if not args.B: res = store.select_one(table.adata, {\"A\": args.A}) return {\"B\": res.value.bval} return {} See weather app code:demo for an example of using suggest.","validating-params#Validating Params":"The run handler can validate the parameters. If there are errors, it can return a validation error like\napp.star def run(dry_run, args): if args.dir == \".\" or args.dir.startswith(\"./\") or args.dir == \"..\" or args.dir.startswith(\"../\"): return ace.result(\"Validation failed\", param_errors={\"dir\": \"relative paths not supported\"}) if dry_run: return ace.result(\"Validation successful\") # Actual code for run handler Errors can be reported for multiple params. If the action definition has show_validate=True, then a Validate option will show up in the UI. Calling that will invoke the run handler with dry_run=True. The run handler should return after the param validation when dry_run is true."},"title":"Action Apps"},"/docs/app/":{"data":{"":"Details about developing Hypermedia driven Clace applications, managing API routes and HTML templates.\nOverviewOverview of a Clace app, sample apps Request RoutingDefining HTML, API and Proxy routes RequestDetails about the request structure passed to the handler ResponseDetails about how handler response is handled "},"title":"Hypermedia Apps"},"/docs/app/overview/":{"data":{"":"Clace supports deploying any type of app, in any language/framework, using containerized apps. Apps can also be built where the backend API is in the container but the UI is built using Clace. Clace UI applications implement a Hypermedia driven approach for developing web applications. Applications return HTML fragments as API response using Go html templates. The UI uses HTML enhanced with hypermedia controls using the HTMX library to implement user interactions.\nThe backend API routes and dependencies like CSS library, JavaScript modules etc are configured using Starlark configuration. Any custom API handling required is implemented in handler functions also written in Starlark. Starlark is a subset of python, optimized for application configuration use-cases.","app-types#App Types":"Clace apps can be of few different types:\nAction apps: Actions allow apps to expose an autogenerated UI for simple backend actions. Containerized apps: The whole app is implemented in a container, Clace proxies the container results. This uses the container and proxy plugins. No Starlark code is required for this. Starlark apps: These use the Clace plugins to implement the whole app. No containers are required for such apps. Hybrid apps: The backend APIs are implemented in a container. Clace is used to implement the Hypermedia based UI using Starlark handlers. This uses the http plugin to talk to the backend API and the container plugin to configure the backend. This section of the docs covers Starlark and Hybrid apps. For a containerized app, the --spec definition includes all the app definition. There is no need to do any custom Starlark config for a regular containerized app.\nSample Starlark App To create an app with a custom HTML page which shows a listing of files in your root directory, create an ~/myapp4/app.star file with\napp.starload(\"exec.in\", \"exec\") def handler(req): ret = exec.run(\"ls\", [\"-l\", \"/\"]) if ret.error: return {\"Error\": ret.error, \"Lines\": []} return {\"Error\": \"\", \"Lines\": ret.value} app = ace.app(\"hello4\", custom_layout=True, routes = [ace.html(\"/\")], permissions = [ace.permission(\"exec.in\", \"run\", [\"ls\"])] ) and an ~/myapp4/index.go.html file with\nindex.go.html\u003c!doctype html\u003e \u003chtml\u003e \u003chead\u003e \u003ctitle\u003eFile List\u003c/title\u003e {{ template \"clace_gen_import\" . }} \u003c/head\u003e \u003cbody\u003e {{ .Data.Error }} {{ range .Data.Lines }} {{.}} \u003cbr/\u003e {{end}} \u003c/body\u003e \u003c/html\u003e Run clace app create --auth=none --dev --approve ~/myapp4 /hello4. After that, the app is available at /hello4. Note that the --dev option is required for the clace_gen_import file to be generated which is required for live reload.\nThis app uses the exec plugin to run the ls command. The output of the command is shown when the app is accessed. To allow the app to run the plugin command, use the clace app approve command.\n⚠️ Note: If running on Windows, change ls to dir. Else, use the fs plugin to make this platform independent. See https://github.com/claceio/apps/blob/main/system/disk_usage/app.star. Custom Layout HTML App To return HTML response, a HTML template file named *.go.html is required. Create an ~/myapp2/app.star file containing\napp.starapp = ace.app(\"hello2\", custom_layout=True, routes = [ace.html(\"/\")] ) and an ~/myapp2/index.go.html file containing\nindex.go.htmlhello world2 Run clace app create --auth=none ~/myapp2 /hello2. After that, the app is available at /hello2\n$ curl localhost:25222/hello2 hello world2 The ~/myapp2/index.go.html can be updated to have a complete HTML page. Use the command clace app reload --promote /hello2 to pick up changes. This app is using custom_layout=True which means the app developer has to provide the complete HTML.\nDefault Layout HTML App The default is custom_layout=False meaning app developer has to provide only the HTML body, Clace will automatically generate the rest of the HTML. For using the auto generated HTML templates, the app has to be created in dev mode using the --dev option.\nCreate an ~/myapp3/app.star file containing\napp.starapp = ace.app(\"hello3\", routes = [ace.html(\"/\")] ) and an ~/myapp3/app.go.html file containing\napp.go.html{{block \"clace_body\" .}} hello world3 {{end}} Run clace app create --auth=none --dev ~/myapp3 /hello3 . After that, the app is available at /hello3. Note that the --dev option is required for the index_gen.go.html file to be generated.\nThere is only one route defined, for page /, which shows a HTML page with the name of the app. The body is generated from the contents of the app.go.html file. A more verbose way to write the same app config would be\napp.starapp = ace.app(name=\"hello3\", custom_layout=False, routes = [ace.html(path=\"/\", full=\"index_gen.go.html\")] ) ","automatic-error-handling#Automatic Error Handling":"To enable automatic error handling (recommended), add an error_handler function like:\napp.stardef error_handler(req, ret): if req.IsPartial: return ace.response(ret, \"error\", retarget=\"#error_div\", reswap=\"innerHTML\") else: return ace.response(ret, \"error.go.html\") "},"title":"Overview"},"/docs/app/request/":{"data":{"":"","accessing-inputs#Accessing Inputs":"Url Parameters For a route defined like\napp.starace.html(\"/user/{user_id}/settings\", \"index.go.html\") the url parameter user_id can be accessed in the handler\napp.stardef handler(req) user_id = req.UrlParams[\"user_id\"] Wildcard parameters are allowed at the end of the route. These are defined as\napp.starace.html(\"/path/*\", \"index.go.html\") and can be accessed as\napp.stardef handler(req) user_id = req.UrlParams[\"*\"] Regexes are also allowed in the path, these are defined as ace.html(\"/articles/{aid:^[0-9]{5,6}}\") and accessed as req.UrlParams[\"{aid}\"]. The route will match only if the regex matches.\nQuery String Parameters Query string parameters can be accessed as\napp.stardef handler(req) name = req.Query.get(\"name\") name = name[0] if name else None The value for Query is an string array, since there can be multiple query parameters with the same name.\nForm Data Form data can be accessed like\napp.stardef handler(req) name = req.Form.get(\"name\") name = name[0] if name else None The value for Form is an string array, since there can be multiple form parameters with the same name.","request-structure#Request Structure":"The handler function is passed one argument which has the details about the API call. The fields in this structure are:\nProperty Type Notes AppName string The app name in the config AppPath string The path where the app is installed. If root, then empty string AppUrl string The url for the app root PagePath string The path for the current page. If root, then empty string PageUrl string The url for the current page Method string The HTTP method, GET/POST etc IsDev bool Is the app installed in dev mode IsPartial bool Is this an HTMX driven partial request RemoteIP string The Client IP address UrlParams dict The url parameters, if used in the url spec Form dict The form data, including body and query Query dict The url query data, as a string array PostForm dict The form data from the body Data dict The response from the handler function (passed to the template) "},"title":"Request"},"/docs/app/response/":{"data":{"":"","custom-response#Custom Response":"In some cases, a custom response need to be generated, with special headers. Or the response needs to use a template different from the one defined in the route, which could happen in the case of an error. For such cases, a ace.Response structure can be returned by the handler. The fields in this structure are:\nProperty Optional Type Default Notes data false object The response data block true string Optional only if type is “json” type true string inherited from the route type definition If “json”, block is ignored code true int 200 HTTP status code retarget true string HX-Retarget header value, CSS selector to target, like “#error_id” reswap true string HX-Reswap, like “outerHTML” For example, this handler code uses retarget to handle errors by updating the html property which has id “gameErrorId”\napp.starret = http.post(api_url).json() if ret.get(\"Error\"): return ace.response(ret, \"game_error_block\", retarget=\"#gameErrorId\") return fetch_game(req, game_id) This code returns a 404 with a custom body generated from a template block called “invalid_challenge_block”\napp.starif challenge.get(\"Error\"): return ace.response(challenge, \"invalid_challenge_block\", code=404) ","json-response#JSON Response":"All responses are HTML by default, as required for building a proper Hypermedia client. There are some cases where data needs to be returned to the client in JSON format. The type property can be used for those cases. For example, this API returns JSON\napp.starace.api(\"/memory\", handler=memory_handler), Here, the response from the handler function is returned as JSON, no template is used. Also, in this handler, if there is a call to ace.Response, the type will default to JSON since that is the type specified at the route level. Mime type detection based on the Accept header is planned, it is not currently supported.","redirect-response#Redirect Response":"If the API needs to redirect the client to another location after a POST/PUT/DELETE operation, the handler function can return an ace.Redirect structure. The fields in this structure are:\nProperty Optional Type Default Notes url false string The url to redirect to code true int 303 The HTTP status code, 303 or 302 For example, this code does a 303 redirect after a POST API, which provides handling for update requests.\napp.stardef create_game(req): level = req.Form[\"level\"] ret = http.post(SERVICE_URL + \"/api/create_game/\" + level[0]) return ace.redirect(req.AppPath + \"/game/\" + ret.json()[\"GameId\"]) ","response-data#Response Data":"The Response from the handler function is passed to the template to be converted to HTML. The handler response is accessible through .Data, or $Data if in another scope. Any python object can be used as the return value. Using a dictionary is recommended, so that error handling is easier. Adding a Error key in the response dict can indicate to the template that an error condition needs to be handled.\nFor example, a handler like\napp.stardef handler(req): name = req.Query.get(\"name\") if name: return {\"Name\": name[0], \"Error\": None} else: return {\"Error\": \"Name not specified\", \"Name\": None} app = ace.app(\"test\", routes = [ace.html(\"/\")]) allows the template to handle the error by doing\n{{block \"clace_body\" .}} {{if .Data.Error}} \u003cdiv style=\"color: red\"\u003e{{.Data.Error}}\u003c/div\u003e {{else}} Hi {{.Data.Name}} {{end}} {{end}} "},"title":"Response"},"/docs/app/routing/":{"data":{"":"The request routing layer in Clace is built on top of the chi routing library. API requests (JSON or text), HTML requests and proxied requests are supported. For HTML requests, the routing is built for hypermedia exchange, so all routes are defined in terms of pages and fragments within the pages. This grouping of requests helps make it clear which API does what and provide an easy mechanism to deal with partial HTMX driven requests and full page refreshes. Simpler application might have one page with some interactions within that. Larger applications can be composed of multiple pages, each page having some interactive fragments.","api-route#API Route":"An API route defines a route which returns JSON (default) or plain text response. The handler response is converted to the desired format and returned. The parameters for ace.api are:\nProperty Optional Type Default Notes path False string The route, should start with a / handler True function handler (if defined) The handler function to use for the route method True string GET The HTTP method type: GET,POST,PUT,DELETE etc, for example ace.GET type True string JSON The response type, ace.JSON or ace.TEXT For example\napp.star def handler(req): return {\"a\": 1} app = ace.app(\"api\", routes = [ ace.api(\"/myapi\") ] ) A GET request to /myapi endpoint will return JSON {\"a\": 1}.","fragment#Fragment":"The fragments array in the html page definition defines the API interactions within the page. The parameters for ace.Fragment are:\nProperty Optional Type Default Notes path False string The route, should not start with a / partial True string Inherited from page The template to use for partial requests handler True function Inherited from page The handler function to use for the route method True string GET The HTTP method type: GET,POST,PUT,DELETE etc, for example ace.GET ℹ️ partial and handler are inherited from the page level, unless overridden for the fragment. For example, in this page definition\napp.starace.html(\"/game/{game_id}\", full=\"game.go.html\", partial=\"game_info_tmpl\", handler=game_handler, fragments=[ ace.fragment( \"submit\", method=ace.POST, handler=lambda req: post_game_update(req, \"submit\")), ace.fragment( \"refresh\", partial=\"refresh_tmpl\") ] ) there are three API’s defined:\nGET /game/{game_id} : game_handler is the handler function, full page request returns game.go.html, partial HTMX request returns game_info_tmpl template. POST /game/{game_id}/submit : The handler is a lambda function. The game_info_tmpl template partial is inherited from the page as the response for the POST. GET /game/{game_id}/refresh : game_handler is inherited from the page. For full page, it returns the game.go.html response. For partial HTMX requests, refresh_tmpl template is returned. ","html-route#HTML Route":"An HTML route defined using ace.html defines the properties for a HTML page. The response is HTML text. The data returned by the handler function is passed to the template. If the handler returns a ace.response or ace.redirect, then that takes effect, otherwise the template is rendered.\nAn HTML route can have fragments defined within it. These are sub-apis which used for hypermedia driven interactions from the main page.\nThe parameters for ace.html are:\nProperty Optional Type Default Notes path False string The route, should start with a / full True string index.go.html if custom layout, else index_gen.go.html The template to use for full page requests partial True string None The template to use for partial page requests handler True function handler (if defined) The handler function to use for the route fragments True ace.fragment[] [] The fragment array method True string GET The HTTP method type: GET,POST,PUT,DELETE etc, for example ace.GET ","notes#Notes":" For HTMX requests, the partial template is used. For regular requests, the page level full template is used If there is a function called handler defined, that is the default handler function for all API’s For non-HTMX update requests (POST/PUT/DELETE), the Post-Redirect-Get pattern is automatically implemented by redirecting to the location pointed to by the Referer header. ","proxy-route#Proxy Route":"A Proxy route defines a route which has to be proxied to another service. All API calls under that route are proxied (all methods and all sub-routes). Websocket connections are also proxied. Proxy uses a plugin based config, the app has to be authorized to do the proxying. The parameters for ace.Proxy are:\nProperty Optional Type Default Notes path False string The route, should start with a / config False ProxyConfig The proxy configuration to use The proxy configuration proxy.config has the options:\nProperty Optional Type Default Notes url False string The url to forward the requests to strip_path True string Additional path components to strip from the request path. The app installation path is always stripped. preserve_host False boolean Whether to preserve the Host header. Default false, the Host header is set to the target host value For example, an app which forwards requests to www.google.com is\napp.starload(\"proxy.in\", \"proxy\") app = ace.app(\"Proxy\", routes=[ ace.proxy(\"/\", proxy.config(\"https://www.google.com\")), ], permissions=[ ace.permission(\"proxy.in\", \"config\", [\"https://www.google.com\"]), ], ) The plugin is authorized to allow proxying to https://www.google.com. Any request to the app will be forwarded after stripping the app path. So if app is installed at /test, a request to /test/abc/def will be forwarded to /abc/def. In addition, if the proxy is configured with strip_path=\"/abc\", then the request will be sent to /def.\nIf proxying is enabled for / route, then /static file serving is disabled for the app since requests to static path are also forwarded to the upstream service. /static_root serving is available and overrides the proxy config.\nSee proxy plugin for details about the proxy config.\n⚠️ Note: If the upstream service service uses relative paths, then all requests are automatically proxied. If the service uses absolute paths, then it better that the app is installed at the root path, like example.com: instead of example.com:/test. If the service uses absolute path including the domain name, then the client will see the absolute path and those requests will not come through the proxy. The HTML body is not rewritten by Clace to rewrite path references. The upstream service needs to use relative paths to ensure that all requests come through Clace. ","request-flow#Request Flow":"The API flow is\nThe API is first sent to the matching app Within the app, the API is routed based on the routes defined If there is a handler defined for the matched route, the handler function is called with the request as argument The response template is invoked, with an input map containing a Data property as returned by the handler function If the API type is set to json, the handler response is directly returned, with no template being used If automatic error handling is enabled (error_handler is defined), then the error handler function is called if there is a error during the handler invocation. The error handler does the response processing, the templates defined in the route are not used. ","routes#Routes":"The app contains an routes array, which defines all the routes for the app. For example, the app definition\napp.starapp = ace.app(\"hello1\", routes = [ ace.html(\"/\"), ace.html(\"/help\", \"help.go.html\") ] ) defines two routes. / routes to the default index page, /help routes to the help page. Routes can be of three types: HTML, API and Proxy."},"title":"Routing"},"/docs/applications/":{"data":{"":"Details on how to install and manage Clace apps.\nOverviewOverview of how Clace apps are installed and managed RoutingDetails about how API routing is done for Clace app App LifecycleVarious types of apps: dev, prod, staging and preview; app promotion App SecurityApp security and sandboxing details Audit EventsAuditing and viewing events "},"title":"Managing Apps"},"/docs/applications/appsecurity/":{"data":{"":"Clace applications run in a sandbox environment with no direct access to the system or Clace service. All access is through plugins. When an application is installed, the admin can audit the application for what access is being requested. Only the permissions which are approved are allowed at runtime.","roadmap#Roadmap":"The following enhancements are planned for the security model\nFor the secrets management feature, the app does not have direct access to the secret value. Apps can access any secret from the secret manager currently. An improvement planned to to allow restricting apps to specific secrets. ","sample-application#Sample Application":"As an example, the disk usage analysis app requires two permissions\napp.starapp = ace.app(\"Disk Usage\", routes=[ace.html(\"/\", partial=\"du_table_block\")], permissions=[ ace.permission(\"exec.in\", \"run\", [\"du\"]), ace.permission(\"exec.in\", \"run\", [\"readlink\"]) ], style=ace.style(\"https://unpkg.com/mvp.css@1.14.0/mvp.css\"), ) It requests permission to use the exec.in plugin to run two CLI commands, first being du and other being readlink. When installing the app\n$ clace app create ./examples/disk_usage/ /utils/disk_usage App audit results /utils/disk_usage : app2WPQHwr5ZpKELqh0TvP5YMSnbab Plugins : exec.in Permissions: exec.in.run [du] exec.in.run [readlink] App created. Permissions need to be approved an audit report is shown with these requests. To approve the requested permissions, the admin can do\n$ clace app approve /utils/disk_usage App audit: /utils/disk_usage Plugins : exec.in Permissions: exec.in.run [du] exec.in.run [readlink] App permissions have been approved. The approval can be done during the app create itself, in that case the app is installed and approved immediately. None of the plugin code runs during the app creation, even for calls at the global scope. If the audit report does not match expectations, the app can be deleted.\n$ clace app create --approve ./examples/disk_usage/ /utils/disk_usage App audit results /utils/disk_usage : app2WPQpws6C1mWb6BujYGOdWMnF1C Plugins : exec.in Permissions: exec.in.run [du] exec.in.run [readlink] App created. Permissions have been approved $ clace app delete /utils/disk_usage App deleted /utils/disk_usage Once the app is created, if the application code is updated to change the line from\napp.star ret = exec.run(\"readlink\", [\"-f\", current], process_partial=True) to\napp.star ret = exec.run(\"rm\", [\"-f\", current], process_partial=True) The app will fail at runtime with an error like\napp /utils/disk_usage is not permitted to call exec.in.run with argument 0 having value \"rm\", expected \"readlink\". Update the app or audit and approve permissions The app cannot be run until either the code change is reverted or the admin approves the new call to rm.","security-model#Security Model":"The security model used by Clace is:\nThe application code written in Starlark(python) and HTML templates is untrusted. The Clace service and plugin code (in Go) are trusted. The admin can audit and approve the access required by the untrusted application code when the app is being installed. After installation, further application code updates do not require any further audit, as long as no new permissions are required. If the updated app code requires any new permission, the new plugin call will fail at runtime with a permission error. The trust boundary is about what the application can do in the backend. The frontend code is sandboxed by the browser, there is no additional auditing implemented for the frontend code.\nFor apps which make calls to external programs (using exec plugin) and containerized apps, the external program runs as usual. The Clace security model applies for Starlark apps. For other apps, the security model allows control on which program to run and what args to pass and which container to use. But there is no restriction on what the external program or container itself can do.","usecases#Usecases":"This security model allows for the following:\nUsers can download applications and run on their machine, without worrying about what operations the app can do on their system outside the audited permissions. Operations teams can install and approve applications. Further application updates can be handled by the development team, without requiring the operational admins to verify the updated code. As long as the application works within the originally defined permission boundary, application updates will continue to work. Application developers can use LLM powered automated code generation tools without worrying about the side-effects of the code. If the generated code tries to perform any operation not previously approved, it will fail. "},"title":"Application Security"},"/docs/applications/audit/":{"data":{"":"Clace automatically creates audit events for all operations. See demo for a demo of events viewing.","configuration#Configuration":"The configurable options related to audit events are:\napp_config.audit.redact_url: Set to true to redact the API path for HTTP events. By default, the API path, except for query string, is logged app_config.audit.skip_http_events: Set to true to skip HTTP event logging The app config options can be set globally in the clace.toml. It can also be set individually for an app by setting the app metadata. For example,\nclace app update-metadata conf --promote 'audit.redact_url=true' /myapp The retention for audit events is configurable globally. The config settings in clace.toml are:\nsystem.http_event_retention_days : Number of days to retain http events, default 90 system.non_http_event_retention_days : Number of days to retain non-http events, default 180 ","custom-events#Custom Events":"HTTP, System and Action events are generated automatically. Apps can also define custom events. To add a custom event, in a handler function, add a call to ace.audit. The parameters for ace.audit are:\nProperty Optional Type Default Notes operation false string The operation type to log target false string The target the operation is being done on detail true string Detailed info about the event For example, the dictionary app does:\nace.audit(\"word_lookup\", args.word) This will enable searching the audit events (using the Viewer app) for all operation of type “word_lookup”.\nOnly the last call to ace.audit from a handler function is logged.","event-viewer#Event Viewer":"Events can be viewed by admin using the Event Viewer app code: demo. To install the app on your instance, run\nclace app create --approve github.com/claceio/apps/clace/audit_viewer /events The event viewer shows events for all apps. This app should be installed for access by admins only.","introduction#Introduction":"All operations against the Clace server are automatically logged in a database. The default location for this database is $CL_HOME/metadata/clace_audit.db. This can be configured by setting the property\nclace.toml[metadata] audit_db_connection = \"sqlite:$CL_HOME/metadata/clace_audit.db\" The events which are logged are\nAll HTTP request except GET, HEAD and OPTIONS System events, like app updates and any metadata changes Action invocations (suggest, validate and exec) Custom events, as defined in app code "},"title":"Audit Events"},"/docs/applications/lifecycle/":{"data":{"":"","application-types#Application Types":"A Clace application can be one of four types:\nDevelopment Apps : Used for developing apps, supports live reload from code change on disk. Production Apps : For production use. Can be created from git hosted source or from sources on local disk. Staging Apps : For reviewing code and config changes before they are pushed to prod. Every prod app has one staging app. Preview Apps : For creating a review environment for code changes, useful as part of code review. ","development-apps#Development Apps":"Development mode apps are used for developing or updating Clace apps. The source for these apps has to be on local disk, it cannot be git. Any code or config changes are live reloaded immediately for dev apps. To create a dev mode app, add the --dev option to the app create command. For example,\nclace app create --dev --approve /home/user/mycode /myapp ","github-reload#GitHub Reload":"The rules for fetching source code from local disk and GitHub are:\nIf the source url starts with http://, https:// or github.com, the source is assumed to be from a github API endpoint. Otherwise the source is assumed to be local disk on the Clace server. If Clace client and server are on different machines and local disk is being used, the code needs to be copied to the server node first. For GitHub source, the format is https://domain_name/org_name/repo_name/sub/folder, like github.com/claceio/clace/examples/disk_usage. The sub_folder should contain the app.star config file. During app create and app reload, the commit id takes precedence over the branch name if both are specified. During app reload, if no branch and commit are specified, the newest code is checked out from the current branch. main is used as current branch if no branch was previously specified for the app. ","preview-apps#Preview Apps":"Preview allows the creation of any number of linked preview apps for a main app. This is supported for apps created from GitHub source. The commit id to use needs to be specified when creating the preview. For example,\nclace preview create 49182d4ca1cacbd8e3463a77c2174a6da1fb66c9 /myapp creates an app accessible at /myapp_cl_preview_49182d4ca1cacbd8e3463a77c2174a6da1fb66c9 which runs the app code in the specified commit id.\nPreview apps cannot be changed once they are created. If preview app requires new permissions, add the --approve option to the preview create command.","production-apps#Production Apps":"Without the --dev options, apps are created as production apps by default. Production apps can be created from source on GitHub or from local disk. In either case, the source code for the app is uploaded to the Clace metadata database. For example:\nclace app create --approve /home/user/mycode example.com:/ creates an production app. After app creation, the original source location is not read, until a app reload operation is done to update the sources. The source folder /home/user/mycode can be deleted if reload is not required, since the sources are present in the Clace metadata database. Every production app automatically has one staging app associated with it.","promoting-changes#Promoting Changes":"When there are code changes, running app reload will update the staging environment.\nclace app reload example.com:/ Reloaded apps: example.com:/_cl_stage 1 app(s) reloaded, 0 app(s) approved, 0 app(s) promoted. The staging app is version 2 now, prod app is still at version 1.\nclace app list -i Id Type Version GitCommit GitBranch Domain:Path SourceUrl app_prd_2aMvX3fc9fH18n6i2Jew0tNxnky PROD* 1 example.com:/ /home/user/mycode app_stg_2aMvX3fc9fH18n6i2Jew0tNxnky STG 2 example.com:/_cl_stage /home/user/mycode At this point, going to the url example.com:/_cl_stage will show the updated code while example.com:/ has not been updated.\nℹ️ The * next to PROD indicates that there are staged changes waiting to be promoted to PROD. To promote the changes to prod, run app promote\nclace app promote example.com:/ Promoting example.com:/ 1 app(s) promoted. The prod app is at the same version as the staging app now\nclace app list -i Id Type Version GitCommit GitBranch Domain:Path SourceUrl app_prd_2aMvX3fc9fH18n6i2Jew0tNxnky PROD 2 example.com:/ /home/user/mycode app_stg_2aMvX3fc9fH18n6i2Jew0tNxnky STG 2 example.com:/_cl_stage /home/user/mycode If the application code change requires new permissions, the reload operation will fail unless --approve is added.\nTo do the reload, approval and promotion is one step, do clace app reload --approve --promote example.com:/.","staging-apps#Staging Apps":"Staging apps are created for each production app. The purpose of staging app is to be able to verify config and code changes before they are made live in the prod app. For example, after the previous app create command, a call to app list with the --internal option will show two apps:\nclace app list --internal Id Type Version GitCommit GitBranch Domain:Path SourceUrl app_prd_2aMvX3fc9fH18n6i2Jew0tNxnky PROD 1 example.com:/ /home/user/mycode app_stg_2aMvX3fc9fH18n6i2Jew0tNxnky STG 1 example.com:/_cl_stage /home/user/mycode The second app is the staging app for the first. app list shows only the main apps by default, the --internal option makes it show the linked apps.\nThe staging app url is available by suffixing _cl_stage at the end of the app path. So for an app at https://example.com/, the staging url is https://example.com/cl_stage. For an app at https://example.com/utils/app1, the staging app url is https://example.com/utils/app1_cl_stage.","write-mode-access#Write Mode Access":"Staging and Preview apps have read only access by default to plugin APIs. This means that when they make calls to plugin APIs, only APIs defined as READ by the plugin are permitted. The HTTP plugin defines GET/OPTIONS/HEAD requests as READ type, POST/PUT/DELETE are defined as WRITE. For the CLI Exec plugin, the run API is defined as WRITE since the CLI command run might do write operations.\nFor cases where the plugin defines an API as Write, the app permission can overwrite the default type and define the operation to be a READ operation. For example, the disk_usage app runs the du command, which is a read operation. The app config defines the run plugin call as type=\"READ\", over-riding the default WRITE type defined in the plugin. If no type is specified in the permission, the type defined in the plugin takes effect.\nStaging and Preview apps are allowed only READ calls by default, even if the app permissions allow WRITE operations. To allow stage apps access to WRITE operations, run clace app update-settings stage-write-access true all. Change all to the desired app glob pattern.\nTo allow preview apps access to WRITE operation, run clace app update-settings preview-write-access true example.com:/. This changes the existing preview apps and any new preview apps created for example.com:/ to allow write operations, if the permissions have been approved."},"title":"Application Lifecycle"},"/docs/applications/overview/":{"data":{"":"The various commands for managing Clace apps are\n$ clace NAME: clace - Clace client and server https://clace.io/ USAGE: clace [global options] command [command options] [arguments...] COMMANDS: server Manage the Clace server app Manage Clace apps preview Manage Clace preview apps account Manage Clace accounts param Manage app parameter values version Manage app versions app-webhook Manage app level webhooks password Generate a password bcrypt config entry help, h Shows a list of commands or help for one command $ clace app NAME: clace app - Manage Clace apps USAGE: clace app command [command options] [arguments...] COMMANDS: create Create a new app list List apps delete Delete an app approve Approve app permissions reload Reload the app source code promote Promote the app from staging to production update-settings Update Clace apps settings. Settings changes are NOT staged, they apply immediately to matched stage, prod and preview apps. update-metadata Update Clace app metadata. Metadata updates are staged and have to be promoted to prod. Use \"clace param\" to update app parameter metadata. help, h Shows a list of commands or help for one command The app management subcommands are under the app command. The preview command manages preview app for an app and version command manages versions for an app. The account command is for managing accounts for an app.","app-authentication#App Authentication":"By default, apps are created with the system authentication type. System auth uses admin as the username. The password is displayed on the screen during the initial setup of the Clace server config.\nTo change app to be un-authenticated, add --auth none to the app create command. After an app is created, the auth type can be changed by running app update-settings auth none /myapp. OAuth based authentication is also supported, see authentication for details.\n⚠️ Changes done to the app settings using the app update-settings command are not staged or versioned, they apply immediately to the stage/prod/preview apps. App settings are fundamental properties of the app, like what authentication type to use, what git auth key to use etc.\nAll other changes done to app metadata using app update-metadata, app reload, param update, account link command, (like account linking, permission approval and code reload) are staged before deployment. Use the --promote option on the change to promote the change immediately when applying it on the staging app. Use app promote command to promote later. When a promotion is done, all currently staged changes for that app are promoted, not just the most recent change. After promote, the prod app is exactly same as staging app.","app-listing#App Listing":"Use the app list command to list apps. If the --internal option is used, then the internal staging and preview apps for each matched prod app are also listed.\n$ clace app list Id Type Version Auth GitInfo Domain:Path SourceUrl app_prd_2d3kCRk43FOuIGy979NbBWakRMm PROD 3 NONE main:9ce5e1adfc28983f7894 memory.demo.clace.io:/ github.com/claceio/clace/examples/memory_usage/ app_prd_2d3kCZ28w6VIzoXMsT9yldwijxL PROD 3 NONE main:9ce5e1adfc28983f7894 cowbull.co:/ github.com/claceio/clace/examples/cowbull app_prd_2d3kCjmw8ldQ2LaOd0CLmWXDApq PROD 3 NONE main:9ce5e1adfc28983f7894 / github.com/claceio/clace/examples/demo app_prd_2d3kKVvSsSgUHqtNGZaD95NuLK3 PROD 3 NONE main:9ce5e1adfc28983f7894 du.demo.clace.io:/ github.com/claceio/clace/examples/disk_usage/ app_prd_2d6KcZmNwHIB8cSzNCotqBHpeje PROD 5 NONE main:ed7545ae739dfe85140a utils.demo.clace.io:/bookmarks github.com/claceio/apps/utils/bookmarks $ clace app list -i Id Type Version Auth GitInfo Domain:Path SourceUrl app_prd_2d3kCRk43FOuIGy979NbBWakRMm PROD 3 NONE main:9ce5e1adfc28983f7894 memory.demo.clace.io:/ github.com/claceio/clace/examples/memory_usage/ app_stg_2d3kCRk43FOuIGy979NbBWakRMm STG 3 NONE main:9ce5e1adfc28983f7894 memory.demo.clace.io:/_cl_stage github.com/claceio/clace/examples/memory_usage/ app_prd_2d3kCZ28w6VIzoXMsT9yldwijxL PROD 3 NONE main:9ce5e1adfc28983f7894 cowbull.co:/ github.com/claceio/clace/examples/cowbull app_stg_2d3kCZ28w6VIzoXMsT9yldwijxL STG 3 NONE main:9ce5e1adfc28983f7894 cowbull.co:/_cl_stage github.com/claceio/clace/examples/cowbull app_prd_2d3kCjmw8ldQ2LaOd0CLmWXDApq PROD 3 NONE main:9ce5e1adfc28983f7894 / github.com/claceio/clace/examples/demo app_stg_2d3kCjmw8ldQ2LaOd0CLmWXDApq STG 3 NONE main:9ce5e1adfc28983f7894 /_cl_stage github.com/claceio/clace/examples/demo app_prd_2d3kKVvSsSgUHqtNGZaD95NuLK3 PROD 3 NONE main:9ce5e1adfc28983f7894 du.demo.clace.io:/ github.com/claceio/clace/examples/disk_usage/ app_stg_2d3kKVvSsSgUHqtNGZaD95NuLK3 STG 3 NONE main:9ce5e1adfc28983f7894 du.demo.clace.io:/_cl_stage github.com/claceio/clace/examples/disk_usage/ app_prd_2d6KcZmNwHIB8cSzNCotqBHpeje PROD 5 NONE main:ed7545ae739dfe85140a utils.demo.clace.io:/bookmarks github.com/claceio/apps/utils/bookmarks app_stg_2d6KcZmNwHIB8cSzNCotqBHpeje STG 5 NONE main:ed7545ae739dfe85140a utils.demo.clace.io:/bookmarks_cl_stage github.com/claceio/apps/utils/bookmarks Use the version list command to list versions for particular apps. This command works on prod app or staging app specifically.\n$ clace version list utils.demo.clace.io:/bookmarks_cl_stage Active Version Previous CreateTime GitCommit GitMessage 1 0 2024-03-01 19:59:27 +0000 UTC 86385ff67deab288c362 Updated bookmarks app 2 1 2024-03-01 20:00:20 +0000 UTC 86385ff67deab288c362 Updated bookmarks app 3 2 2024-03-01 20:16:08 +0000 UTC 86385ff67deab288c362 Updated bookmarks app 4 3 2024-03-02 00:12:28 +0000 UTC 8080fcfe6be832a1f6f5 Update styling for bookmarks app =====\u003e 5 4 2024-03-02 00:23:35 +0000 UTC ed7545ae739dfe85140a Update styling for bookmarks app $ clace version list utils.demo.clace.io:/bookmarks Active Version Previous CreateTime GitCommit GitMessage 1 0 2024-03-01 19:59:27 +0000 UTC 86385ff67deab288c362 Updated bookmarks app 2 1 2024-03-01 20:00:20 +0000 UTC 86385ff67deab288c362 Updated bookmarks app =====\u003e 5 2 2024-03-02 00:23:35 +0000 UTC ed7545ae739dfe85140a Update styling for bookmarks app The version switch command can be used to switch versions, up or down or to particular version. The version revert command can be used to revert the last change. app promote makes the prod app run the same version as the current staging app.\nIn the above listing, the staging app has five versions. Three of those (1,2 and 5) were promoted to prod. version switch previous utils.demo.clace.io:/bookmarks_cl_stage will change the stage app to version 4. version switch previous utils.demo.clace.io:/bookmarks will change the prod app to version 2. After that, app promote utils.demo.clace.io:/bookmarks will change prod to also be at version 4, same as stage. The version switch command accepts previous, next and actual version number as version to switch to.\nA star, like PROD* in the app list output indicates that there are staged changes waiting to be promoted. That will show up any time the prod app is at a different version than the stage app.","app-management#App Management":"All the app management subcommands except create take a glob pattern, so multiple apps can be updated using one command. Use the --dry-run option with any update CLI call to verify if the options specified are correct before the actual run. No database changes are committed during dry-run and no changes are done in memory.","glob-pattern#Glob Pattern":"The reload/delete/promote/approve/list/update commands accept a glob pattern. example.com:** will match apps under the example.com domain. *:** will match all apps in all domains, all is a shortcut for this. When using glob patterns, place the pattern inside double-quotes to avoid issues with shell star expansion. For example, \"example.com:**\"\nThe default for app list command is to list all apps. All other commands require an glob pattern to be specified explicitly.\nWhen multiple apps are being updated, if any one app fails, the whole operation is rolled back. This allows for atomic updates across multiple applications."},"title":"App Management Overview"},"/docs/applications/routing/":{"data":{"":"","creating-applications#Creating Applications":"When an app is being created, a path and an optional domain need to be specified. For the above example scenario:\nNo new apps can be created for example.com domain, since App C is installed at the root path. For the default domain, /utils/appA and /appB are taken, other paths are available. New apps can be created under any path, including root path, for new domains and subdomains, like test.example.com. ","notes#Notes":" The domain specified for the app is used only for routing requests. The user has to ensure that the actual DNS routing is done properly outside of Clace for the API calls to land on the Clace server. Using wildcard DNS entries will reduce the configuration required in the DNS service. So if *.example.com points to the IP address of the Clace service, any domain based routing done in Clace will work with no further DNS configuration being required. The automated certificates created by Clace will be domain level certificates, wildcard certificates are not currently created. /_clace/ API path at the root level and /_clace_app/ within an app path are reserved paths, they are used by the Clace internal APIs, requests will not reach the app. This applies for all domains. _cl_ is reserved for use for internal apps, so app path last component cannot have _cl_. ","request-routing#Request Routing":"The Clace server can host multiple applications simultaneously. Each application gets a dedicated path, the application can use all sub-paths within that without conflicts with other applications.\nWhen an application is created, a path needs to be specified and a domain can be optionally specified. Routing for application requests is done based on domain and path. The domain is the namespace within which the paths are resolved. If no domain is specified for the app, it is installed for the default domain. Default domain can be set using the system.default_domain config property (default value is localhost).\nConsider this scenario:\nApp A is installed at /utils/appA App B is installed at /appB App C is installed at the root level / for domain example.com, example.com:/ For the above scenario:\nRequests to /utils/appA/* for the default domain go to app A Requests to /appB/* for the default domain go to App B. Requests to example.com for any path will always go to App C, since it is installed at the root path. "},"title":"Application Routing"},"/docs/configuration/":{"data":{"":"Most configuration options specified in the following sections are for the Clace server. The Clace client CLI, which talks with the Clace server using unix domain sockets, uses a small subset of the config properties. If the Clace client runs on the same machine as the server, then the same config file can be used for both. See here for details.\nOverviewConfiguration overview, CL_HOME env Ports and CertificatesSetting up ports, TLS certs, certificate signing SecurityAdmin account, admin API access, github repo security App authenticationApp authentication using admin account, OAuth account config Secrets ManagementSecrets management, with AWS secrets manager, env, vault and properties file "},"title":"Configuration"},"/docs/configuration/authentication/":{"data":{"":"By default, apps are created with the system authentication type. The system auth uses admin as the username. The password is displayed on the screen during the initial setup of the Clace server config.\nApps can also be changed to have no authentication, making them publicly accessible. To change app to be un-authenticated, add --auth none to the app create command. After an app is created, the auth type can be changed by running app update-settings auth none /myapp.","callback-url#Callback Url":"To enable any Oauth provider, the callback url domain has to be specified in the server config. Add\nclace.toml[security] callback_url = \"https://example.com:25223\" in the clace.toml. In the OAuth account, for an entry github_test, the callback url to use will be https://example.com:25223/_clace/auth/github_test/callback.\nThe format for the callback url to use is \u003cCALLBACK_URL\u003e/_clace/auth/\u003cPROVIDER_ENTRY_NAME\u003e/callback. The callback url has to exactly match this format.","client-cert-authentication-mtls#Client Cert Authentication (mTLS)":"Apps can be updated to use mutual TLS authentication. To enable this, first set disable_client_certs to false in the https section. Add a client_auth config entry in server config with the CA certificate to verify against. Multiple entries can be added, the entry name should be cert or should start with cert_. For example\nclace.toml[https] disable_client_certs = false [client_auth.cert_test1] ca_cert_file=\"/data/certs/ca1.crt\" [client_auth.cert_test2] ca_cert_file=\"/data/certs/ca2.crt\" defines two client_auth configs: cert_test1 using ca1.crt and cert_test2 using ca2.crt. Apps can be updated to use this auth config by running app update-settings auth cert_test1 /myapp or app update-settings auth cert_test2 /myapp.\nAny API call to the app has to pass the client certificates. Using curl, the call would look like:\ncurl -k --cert client.crt --key client.key https://localhost:25223/myapp If the client cert has been signed with the root CA defined in /data/certs/ca1.crt, the API call will succeed. Otherwise it fails. HTTP requests are not allowed when client cert authentication is used.","default-authentication-type#Default Authentication Type":"Any app when created uses the default auth type configured for the server. system is the default. To change this, add\nclace.toml[security] app_default_auth_type = \"github_prod\" assuming there is a github_prod oauth config.\nAny new app created will use this as the auth unless overridden in the app create call or using app update.","oauth-authentication#OAuth Authentication":"OAuth based authentication is supported for the following providers:\ngithub google digitalocean bitbucket amazon azuread microsoftonline gitlab auth0 okta oidc The configuration format for each is\nclace.toml[auth.github_test] key = \"abcdefgh\" secret = \"mysecret\" Here, the auth config entry name is github_test. The entry name can be one of the supported providers, or a supported provider name followed by a _ and a qualifier. The provider name is case sensitive. So github, google, github_prod, google_my_org etc are valid config names. github-test and my_org_google are not valid.\nThe server clace.toml can have multiple auth configs defined. One of them can be set to be the default using app_default_auth_type config. Apps can be configured to use one of system or none or a valid auth config name as the auth. For example, app 1 can use system and app 2 can use github_test.","oauth-config-details#OAuth Config Details":"The config details depend on the provider type. The key is generally the Client Id and the secret is the Client Secret. For some providers, additional config config entries are supported. These are:\ngoogle: The google provider supports a hosted_domain option. This is the domain name to verify on the user being logged in. For example, this can be set to clace.io. okta: The Okta provider supports the org_url config, the tenant url to verify. auth0: The Auth0 provider supports the domain config. oidc: OIDC supports the discovery_url config property. For all the providers, an optional scopes property is also supported. This is the list of scopes to configure for the OAuth account.\n⚠️ The first time a new provider is added, it is important to manually verify an app, to verify if the required authentication restrictions are in place. For example, with google, any valid google user can login, including gmail.com accounts. The hosted_domain config has to be used to restrict this. The OAuth integration internally uses the goth library, see examples for implementation details."},"title":"App Authentication"},"/docs/configuration/networking/":{"data":{"":"","default-domain#Default Domain":"The config has the default domain set to localhost by default. The default domain is used for any app which is installed without an explicit domain being specified. This can be changed to the required value when configuring the server.\nclace.toml[system] default_domain = \"localhost\" # default domain for apps The list_app app is served at the default domain root level if no app is installed there.\nclace.toml[system] root_serve_list_apps = \"auto\" # \"auto\" means serve list_apps app for default domain, \"disable\" means don't server for any domain, To disable this, set root_serve_list_apps to disable. The list apps app uses the defult authentication as set for the system. If another domain needs to be used, set the value to that.\nThe list_apps app can be installed explicitly from github.com/claceio/apps/clace/list_apps source path. This allows the app to be installed with required auth settings. The listing shows apps which are available unauthenticated and apps which are using the same auth as the one set for the list_apps app.","dev-env-certificates#Dev Env Certificates":"For local dev environment, using the auto generated certs will result in browser warnings when connecting to the HTTPS port. To avoid this, use a tool like mkcert to generate root CA for local env. Install mkcert and then run\nmkcert -install mkcert example.com \"*.example.com\" example.test localhost 127.0.0.1 ::1 cp ./example.com+5.pem $CL_HOME/config/certificates/default.crt cp ./example.com+5-key.pem $CL_HOME/config/certificates/default.key The mkcert generated certificates signed with the local CA will be used after the next Clace server restart.\nFor local env, wildcard DNS will not work without tools like dnsmasq. An easier alternative is to add /etc/hosts entries as required mapping to 127.0.0.1. Using url path routing instead of domain based routing for local env is a convenient option.","enable-automatic-signed-certificate#Enable Automatic Signed Certificate":"Clace uses the certmagic library for fully-managed TLS certificate issuance and renewal for production deployment. Certmagic is disabled by default. To enable, the pre-requisites are:\nThe https config is using 443 as the port number. Running on privileged ports requires additional setup There is an DNS entry created pointing your host name or domain wildcard to the IP address of the host running the Clace server. This has to be done in your DNS provider config. Port 443 is reachable from the public internet. This has to be done in your infrastructure provider network settings. Once the pre-requisites are met, set the service_email config parameter to your email address. This enables certmagic based certificate creation. The config will look like:\nclace.toml# HTTPS port binding related Config [https] host = \"0.0.0.0\" port = 443 enable_cert_lookup = true # enable looking for domain specific certificate files on disk service_email = \"MY_EMAIL@example.com\" # CHANGE to your email address use_staging = true # CHANGE to false for production cert_location = \"$CL_HOME/config/certificates\" storage_location = \"$CL_HOME/run/certmagic\" Test out the certificate creation by sending HTTPS requests to port 443. If the certificate is getting created, change use_staging to false. Let’s Encrypt has strict rate limits, use the staging config to ensure that the pre-requisites are met before using the production config.\nWith this config, certmagic is used to create certificates for all HTTPS requests. Self signed certificates and enable_cert_lookup property are not used when certmagic is enabled.","http#HTTP":"For HTTP requests, by default the Clace service listens on port 25222, on the localhost(127.0.0.1) interface. This means the HTTP port can be accessed from the same machine, it cannot be accessed remotely. To configure this, update the config file\nclace.toml[http] host = \"127.0.0.1\" # bind to localhost by default for HTTP port = 25222 # default port for HTTP to desired values. Port 0 means bind to any available port. Port -1 means disable HTTP access. Use host as 0.0.0.0 to bind to all available interfaces.","https#HTTPS":"For HTTPS requests, the Clace service listens on port 25223 by default, on the any(0.0.0.0) interface. This means the HTTPS port can be accessed from the same machine and also remotely. The various HTTPS config settings are:\nclace.toml# HTTPS port binding related Config [https] host = \"0.0.0.0\" # bind to all interfaces (if port is \u003e= 0) port = 25223 # port for HTTPS enable_cert_lookup = true # enable looking for domain specific certificate files on disk service_email = \"\" # email address for registering with Let's Encrypt. Set a value to enable automatic certs use_staging = true # use Let's Encrypt staging server cert_location = \"$CL_HOME/config/certificates\" # where to look for existing certificate files storage_location = \"$CL_HOME/run/certmagic\" # where to cache dynamically created certificates Port 0 means bind to any available port. Port -1 means disable HTTPS access.\nℹ️ Using the HTTPS port is recommended even for the local environment. HTTP/2 works with HTTPS only. Server Sent Events (SSE) are used by Clace for live reload of dev apps, SSE works best with HTTP/2. Without HTTP/2, there can be connection limit issues with HTTP causing connections from browser to Clace server to hang. ","notes#Notes":" Please provide a valid email address in service_email. This allows you to receive expiration emails and also allows the CA to contact you if required. Start the configuration with staging use_staging = true, change to production config use_staging = false after ensuring that DNS and networking is working fine. If port 0 is used, the service will bind to any available port. Look at the stdout or logs to find the port used. Clients would have to be updated after every server restarted to point to the new port. Only the TLS-ALPN challenge is enabled in Clace. The HTTP and DNS based challenges are not supported currently. If Clace is running behind a load balancer, ensure that the load balancer is doing TLS pass-through. If TLS termination is done in the load balancer, then the automatic certificate management done by Clace through certmagic will not work. ","privileged-ports#Privileged Ports":"On Linux, binding to low ports is disabled for non-root users. To enable binding to port 80 for HTTP and 443 for HTTPS, run the command\nsudo setcap cap_net_bind_service=+ep /path/to/clace_binary This would be required after any new build or update of the Clace binary.","redirect-from-http-to-https#Redirect from HTTP to HTTPS":"To enable automatic redirect from HTTP to HTTPS, add redirect_to_https = true in the http section of the config. Also, change the host to 0.0.0.0. For example,\nclace.toml[http] host = \"0.0.0.0\" port = 80 redirect_to_https = true All requests to the HTTP port will 308 redirect to the HTTPS port.","tls-certificates#TLS Certificates":"In the default configuration, where service_email is empty, certmagic integration is disabled. The certificate handling behavior is:\n$CL_HOME/config/certificates is looked up for a crt and key file in the PEM format matching the domain name as passed to the server. If a matching certificate is found, that is used. If no domain specific certificate is found, then the default certificate default.crt and default.key are looked up. If found, that is used. If default certificate is not found, then a self-signed default certificate is auto created in the certificates folder. The intent is to allow custom certificates to be placed in the certificate folder, which will be used. If not found, a self-signed certificate is created and used. For example, if files example.com.crt and example.com.key are found in the certificates folder, those are used for example.com domain."},"title":"Ports and Certificates"},"/docs/configuration/overview/":{"data":{"":"","clace-client-cli#Clace Client CLI":"By default, the Clace client uses Unix domain sockets to connect to the Clace server. $CL_HOME should point to the same location for server and client. If no changes are done for the server defaults, then the client can connect to the server locally without any other configuration being required. See here for details about the client configuration.","clace-server#Clace Server":"The Clace server picks up its configuration from the config file at startup. All the parameters have default values, specified in the code at clace.default.toml.\nA user-specified config file can be provided. The environment variable CL_CONFIG_FILE is used to locate the config file. If not set, it defaults to $CL_HOME/clace.toml.\nValues in the user specified config take precedence over the default config values in the source code.","home-directory#Home Directory":"The CL_HOME environment variable is used to locate the home directory for the Clace server. If no value is set, this defaults to the directory from which the Clace server was started. This location is used to store:\nThe default config file, $CL_HOME/clace.toml The sqlite database containing the metadata information, default is $CL_HOME/clace.db The logs for the service, under the logs folder. The config folder contains the certificates to be use for TLS. The run folder contains app specific temporary files. "},"title":"Config Overview"},"/docs/configuration/secrets/":{"data":{"":"Clace supports secret management when working with apps. Secrets can be passed to containerized apps through the environment params. Secrets can also be passed to any plugin as argument. For OAuth config, the client secrets can be configured as secret in the config file.","aws-secrets-manager#AWS Secrets Manager":"To enable ASM, add one or more entries in the clace.toml config. The config name should be asm or should start with asm_. For example\nclace.toml[secret.asm] [secret.asm_prod] profile = \"myaccount\" creates two ASM configs. asm uses the default profile and asm_prod uses the myaccount profile. The default config is read from the home directory ~/.aws/config and ~/.aws/credentials as documented in AWS docs. The user id under which the Clace server was started is looked up for the aws config file.\nTo access a secret in app parameters from asm_prod config, use --param MYPARAM='{{secret \"asm_prod\" \"MY_SECRET_KEY\"}}' as the param value.","environment-secrets#Environment Secrets":"Adding a secret provider with the name env or starting with env_, like\nclace.toml[secret.env] enables looking up the Clace server environment for secrets. This can be accessed like --param MYPARAM='{{secret \"env\" \"MY_SECRET_KEY\"}}'. No properties are required in the env provider config. The value of MY_SECRET_KEY in the Clace server env wil be passed as the param.","hashicorp-vault#HashiCorp Vault":"To enable Vault secret provider, add one or more entries in the clace.toml config. The config name should be vault or should start with vault_. For example\nclace.toml[secret.vault_local] address = \"http://127.0.0.1:8200\" token = \"abc\" [secret.vault_prod] address = \"http://myvault.example.com:8200\" token = \"def\" creates two Vault configs. The address and token properties are required.","multiple-keys#Multiple Keys":"If the KEY_NAME is a single string, it is passed as is to the provider. If multiple keys are specified, they are concatenated and passed to the provider. For example, {{secret \"env\" \"ABC\" \"DEF\"}} will get converted to a env lookup for ABC_DEF. The delimiter used depends on the provider. The defaults are:\nASM and Vault : / Env : _ Properties: . The formatter used to concatenate the keys can be customized by setting the keys_printf property. For example,\nclace.toml[secret.prop] file_name = \"/etc/mykeys.properties\" keys_printf = \"%s-%s.%s\" combines {{secret \"prop\" \"ABC\" \"DEF\" \"XYZ\"}} as ABC-DEF.XYZ. This allows the app to work with multiple secret providers without requiring code changes in the app.","properties-secrets#Properties Secrets":"Secrets can be read from a properties file. The config name should be prop or should start with prop_. To use this, add\nclace.toml[secret.prop_test1] file_name = \"/etc/props.properties\" file_name is a required property.","secrets-usage#Secrets Usage":"Secrets can be accessed using the syntax {{secret \"PROVIDER_NAME\" \"KEY_NAME\"}}. The three contexts in which secrets can be accessed are:\nApp Params : Param values in params.star or in the app metadata definition can access the secrets. Plugin arguments : Secrets can be passed as string arguments in calls to plugin functions. Config file: Secrets are supported in clace.toml config for: For client key and secret in auth config For password in git_auth config For string values in plugin config Secrets are always resolved late. The Starlark code does not get access to the plain text secrets. The secret lookup happens when the call to the plugin API is done. In case of params, the lookup happens when the param is passed to the container.\nFor git_auth config, an example secret usage is\nclace.toml[auth.google_prod] key = \"mykey.apps.googleusercontent.com\" secret = '{{secret \"PROVIDER_NAME\" \"GOOGLE_OAUTH_SECRET\"}}' hosted_domain = \"example.com\" ","supported-providers#Supported Providers":"Clace currently supports AWS Secrets Manager (ASM) and HashiCorp Vault as providers for secrets management. Secrets can also be read from the environment of the Clace server, which can be used in development and testing."},"title":"Secrets Management"},"/docs/configuration/security/":{"data":{"":"The default configuration for the Clace server is:\nApplication management (admin APIs) are accessible over unix domain sockets only (not accessible remotely). Since UDS enforces file permissions checks, no additional authentication is needed for admin APIs. Admin user account is used to access applications, default auth for apps is system The admin user password bcrypt hash has to be added to the server config file, or a random password is generated every time the server is restarted Applications can be changed to not require any authentication, auth can be none or use Oauth2 based auth. There is no user management support in Clace currently. The system account is present by default (which can be disabled) or OAuth based auth can be used. ","admin-account-password#Admin Account Password":"When the Clace server is started, it looks for the entry\nclace.toml[security] admin_password_bcrypt = \"\" # the password bcrypt value in the config file. If the value is undefined or empty, then a random password is generated and is used as the admin password for that server session. The password being used is displayed on the stdout of the server startup. This will change on every restart.\nTo configure a value for the admin user password, use the password helper command:\nclace password to generate a random password. This will print out the password and its bcrypt value to the screen. Save the password in your password manager and add the bcrypt hash to your config file.\nTo use a particular value for the admin password, run:\nclace password --prompt This will prompt for the password and print out the bcrypt hash to add to the config file.","admin-api-access#Admin API Access":"By default, the Clace client uses Unix domain sockets to connect to the Clace server. Admin API calls to manage applications are disabled over HTTP/HTTPS by default. Unix sockets work when the client is on the same machine as the server, the client does not need to pass any credentials to connect over unix sockets.\nTo enable remote API calls, where the client is on a different machine from the server, the server needs to be changed to add the following:\nclace.toml[security] admin_over_tcp = true If running the Clace client from a remote machine, the config options required for the client are:\nclace.tomlserver_uri = \"https://\u003cSERVER_HOST\u003e:25223\" admin_user = \"admin\" [client] admin_password = \"\" # Change to actual password skip_cert_check = false # Change to true if using self-signed certs All other server related config entries are ignored by the Clace client. Note that to connect to a Clace server over HTTP remotely, the server needs to be bound to the all interface(0.0.0.0), see here.\nIf server_uri is set to the https endpoint and the Clace server is running with a self-signed certificate, set skip_cert_check = true in config to disable the TLS certificate check.","application-security#Application Security":"See appsecurity for details about the application level sandboxing.","private-repository-access#Private Repository Access":"The app create and app reload commands can read public GitHub repositories. If the repository is private, to be able to access the repo, the ssh key needs to be specified. In the clace.toml config file, create an entry like:\nclace.toml[git_auth.infoclace] key_file_path = \"/Users/myuser/.ssh/infoclace_rsa\" password = \"\" infoclace is the git auth key name, key_file_path points to the location of a private key file for a user with access to the repository. When running app create, add the --git-auth infoclace option. The private key specified will be used for accessing the repository. app reload command will automatically use the same key as specified during the create.\nTo change the git auth key for an app, run:\nclace app update-settings git-auth newkey /myapp "},"title":"Security"},"/docs/container/":{"data":{"":"Details about developing containerized Clace applications.\nOverviewOverview of a containerized Clace app Container ConfigDetails about the container config and state management "},"title":"Containerized Apps"},"/docs/container/config/":{"data":{"":"The default configuration for the Clace server is defined here. The container related config settings are\n[app_config] # Health check Config container.health_url = \"/\" container.health_attempts_after_startup = 30 container.health_timeout_secs = 5 # Idle Shutdown Config container.idle_shutdown_secs = 180 container.idle_shutdown_dev_apps = false # Status check Config container.status_check_interval_secs = 5 container.status_health_attempts = 3 A health check is done on the container after container is started. If the health check fails 30 times, the container is assumed to be down.\nIn the running state, a status check is done on the app every five seconds. If three of those checks fail, then the container is assumed to be down.\nIf an app does not receive any API request for 180 seconds, the app is assumed to be idle and the container is stopped. The idle shutdown does not apply for dev apps, only for prod mode apps.","changing-config#Changing Config":"The clace.toml can be updated to have a different value for any of the properties. After the server restart, the config change will apply for all apps.\nTo apply the config at the app level, the app metadata can be updated. For example the command\nclace app update-metadata conf --promote container.idle_shutdown_secs=600 /myapp changes the idle timeout for the /myapp app to 600 secs. Without the --promote option, the change will be staged and can be verified on the staging app. App metadata level setting take precedence over the defaults in the clace.toml. Using all as the app name will apply the change for all current apps (but not for any new apps created later).","dockerpodman#Docker/Podman":"The default for the container command to use is\n[system] container_command = \"auto\" auto means that Clace will look for podman in the path. If found, it will use that. Else it will use docker as the container manager command. If the value for container_command is set to any other value, that will be used as the command to use.\nOrbstack implements the Docker CLI interface, so Orbstack also works fine with Clace."},"title":"Container Config"},"/docs/container/overview/":{"data":{"":"Clace builds the image and manages the container lifecycle for containerized apps. Clace fetches the source code, creates the image, starts the container, does health checks on the container and stops the container when idle. Appspecs allow existing source code to be used with Clace with no code changes required.","app-environment-params#App Environment Params":"For containerized apps, all params specified for the app (including ones specified in params.star spec) are passed to the container at runtime as environment parameters. CL_APP_PATH is a special param passed to the container with the app installation path (without the domain name). PORT is also set with the value of the port number the app is expected to bind to within the container.\nFor example, the command\nclace app create --approve --spec python-fasthtml \\ --param APP_MODULE=basic_ws:app \\ https://github.com/AnswerDotAI/fasthtml/examples fasthtmlapp.localhost:/ creates a FastHTML based app. The APP_MODULE env param is passed to the Container and passed to the startup command line in the Containerfile.\nTo update params, run\nclace param update APP_MODULE basic_app:app fasthtmlapp.localhost:/ Param updates are staged, they can be promoted after verification. To delete a param, pass - as the value to the update. Use clace param list to view app params.\nParams can be set to secrets, by setting the value as {{secret \"vault_prod\" \"MY_KEY_NAME\"}}. The secret is resolved when the container is started and the value is passed to the container in its env.\nℹ️ Note: Staged param updates are a powerful mechanism to ensure that config changes do not break your apps. For example, if BUCKET_NAME is a param pointing to a S3 bucket, the param change can be staged. The staging app can be tested to ensure that the new bucket is functional and there are no IAM/key related errors. Once the staging app is working, the app can be promoted. Code changes are easy to test, but config changes can cause env specific errors. Configuration related issues are a common cause of outages during deployment. Clace enables you to avoid such errors. ","app-specs#App Specs":"Clace app specs are defined at https://github.com/claceio/appspecs. Most specs use containers, the proxy spec is an exception.\nThe image spec specifies the image to use. for example\nclace app create --spec image --approve --param image=nginx \\ --param port=80 - nginxapp.localhost:/ downloads the nginx image, starts it and proxies any request to https://nginxapp.localhost:25223 to the nginx container’s port 80. The container is started on the first API call, and it is stopped automatically when there are no API calls for 180 seconds.\nFor all other specs, the Containerfile is defined in the spec. For example, for the python-streamlit spec, the Containerfile is here. Running\nclace app create --spec python-streamlit --branch master \\ --approve github.com/streamlit/streamlit-example /streamlit_app will create an app at https://localhost:25223/streamlit_app. On the first API call to the app, the image is built from the defined spec and the container is started. The python-gradio spec does the same for gradio apps.","app-specs-listing#App Specs Listing":"The specs defined currently are:\nSpec Name Required Params Optional Params Supports Path Routing Notes Example container port : The port number within container, optional if EXPOSE directive is present Depends on app Requires app code to have a Containerfile/Dockerfile image image: The image to use for the container port : The port number within container Depends on app No source url required when creating app, use - as url clace app create --spec image --approve --param image=nginx --param port=80 - nginxapp.localhost:/ proxy url: The url to which requests should be proxied No No source url required when creating app, use - as url clace app create --spec proxy --approve -param url=https://clace.io - proxyapp.localhost:/ python-wsgi APP_MODULE: The module:app for the WSGI app. Defaults to app:app, meaning app in app.py Depends on app Runs Web Server Gateway Interface (WSGI) apps using gunicorn python-asgi APP_MODULE: The module:app for the ASGI app. Defaults to app:app, meaning app in app.py Depends on app Runs Asynchronous Server Gateway Interface (ASGI) apps using uvicorn python-flask port : The port number within container. If EXPOSE directive is present, that is used. Defaults to 5000 Depends on app Runs app using flask dev server python-streamlit app_file : The file name of the streamlit app to run. Default streamlit_app.py Yes clace app create --spec python-streamlit --branch master --approve github.com/streamlit/streamlit-example /streamlit_app python-streamlit-poetry app_file : The file name of the streamlit app to run. Default streamlit_app.py Yes Installs packages using poetry python-fasthtml APP_MODULE: The module:app for the ASGI app. Defaults to app:app, meaning app in app.py Depends on app Runs app using uvicorn clace app create --approve --spec python-fasthtml --param APP_MODULE=basic_ws:app https://github.com/AnswerDotAI/fasthtml/examples fasthtmlapp.localhost:/ python-gradio app_file : The file name of the gradio app to run. Default run.py Yes clace app create --spec python-gradio --approve github.com/gradio-app/gradio/demo/blocks_flag /gradio_app go port : The port number within container MAIN_PACKAGE : The go module to build, default “.”. Pass as a --carg instead of --param.APP_ARGS : Args to pass to the app Depends on app CGO is disabled; go.mod has to be present; app should bind to 0.0.0.0 clace app create --approve --spec go --param port=8080 --param APP_ARGS=\"-addr 0.0.0.0:8080\" --branch master github.com/golang/example/helloserver /goapp ","container-build-args#Container Build Args":"If the Containerfile has an argument, the arg can be passed during the app create. Most python specs have the python version as an argument, For example, https://github.com/claceio/appspecs/blob/a06a59a91d99520e271c6f3df68b6fb8292dbf50/python-fasthtml/Containerfile#L2 sets\nARG PYTHON_VERSION=3.12.5-slim To change this during app creation, pass --carg PYTHON_VERSION=3.11.1. For example,\nclace app create --approve --spec python-fasthtml \\ --param APP_MODULE=basic_ws:app \\ --carg PYTHON_VERSION=3.11.1 \\ https://github.com/AnswerDotAI/fasthtml/examples fasthtmlapp.localhost:/ To update args, run\nclace app update-metadata carg PYTHON_VERSION=3.11.2 fasthtmlapp.localhost:/ Like all metadata updates, arg updates are staged. Pass --promote to promote immediately or run app promote to promote from stage to prod.\nℹ️ Note: The slim images are smaller, but they lack some debugging tools. The regular image can be used during development. ","container-options#Container Options":"To set CPU and memory limits and other options for the container, pass --copt optkey[=optvalue] to the app create command. For example, --copt cpu-shares=1000\nclace app create --approve --spec python-fasthtml \\ --param APP_MODULE=basic_ws:app \\ --copt cpu-shares=1000 \\ https://github.com/AnswerDotAI/fasthtml/examples fasthtmlapp.localhost:/ sets the CPU shares for the container to 1000.\nTo update container options, run\nclace app update-metadata copt cpu-shares=500 fasthtmlapp.localhost:/ Like all metadata updates, option updates are staged. Pass --promote to promote immediately or run app promote to promote from stage to prod.\nℹ️ Note: By default there are no limits set for the containers. That allows for full utilization of system resources. To avoid individual apps from utilizing too much of the system resources, CPU/memory limits can be set. "},"title":"Container Overview"},"/docs/develop/":{"data":{"":"","action-apps#Action Apps":"For use cases where an existing CLI application or API needs to be exposed as a web app, actions provide an easy solution. First, define the parameters to be exposed in the form UI. Create a params.star file with the params. For example,\nparams.starparam(\"dir\", description=\"The directory to list files from\", default=\"/tmp\") The app defines a run handler which runs ls on the specified directory. The output text is returned.\napp.starload (\"exec.in\", \"exec\") def run(dry_run, args): out = exec.run(\"ls\", [\"-Lla\"]) if out.error: return ace.result(out.error) return ace.result(\"File listing for \" + args.dir, out.value) app = ace.app(\"List Files\", actions=[ace.action(\"List Files\", \"/\", run, description=\"Show the ls -a output for specified directory\")], permissions=[ ace.permission(\"exec.in\", \"run\", [\"ls\"]), ], ) The app, when accessed will look as shown below, with the ls command output displayed:\nSee list files code:demo for the above app. See dictionary code:demo for another actions example app which shows different type of reports. Actions has more details on building app actions.","app-lifecycle#App Lifecycle":"The Clace app development lifecycle is:\nCreate a folder for the app, with the app.star file and templates. Start the Clace server. Create an app using clace app create --dev. This runs the app in dev mode. In dev mode, some additional files are generated, with _gen in the file name. CSS dependencies and JavaScript modules are downloaded into the static folder. After the app development is done, the whole app folder can be checked into source control. There is no build step. Create a production app, clace app create, without the --dev. The app is now live. The Clace server can host multiple applications, each application has a dedicated path and optionally a dedicated domain. ","app-parameters#App Parameters":"Having a file params.star in the app source code causes Clace to load the parameters definitions from that file. Parameters are environment values which can be specified during app creation. A sample param definition is\nparams.starparam(\"port\", type=INT, description=\"The port the flask app is listening on (inside the container)\", default=5000) param(\"app_name\", description=\"The name for the app\", default=\"Flask App\") param(\"preserve_host\", type=BOOLEAN, description=\"Whether to preserve the original Host header\", default=False) This is defining three parameters. The type can be one of STRING(default), INT, BOOLEAN, LIST and DICT. The param structure definition is\nProperty Optional Type Default Notes name False string Has to be be a valid starlark keyword type True STRING, INT, BOOLEAN, LISTorDICT STRING The data type default True Type as set for type Zero value for the type description True string The description for the param required True bool True If required is True and default value is not specified, then validation fails display_type True string How ths param should be displayed in the UI. Options are FILE, PASSWORD and TEXTAREA, default is text input. The parameters are available in the app Starlark code, through the param namespace. For example, param.port, param.app_name etc. See https://github.com/claceio/appspecs/blob/main/python-flask/app.star for an example of how this can be used.\nParams are set, during app creation using app create --param port=9000 or using param update port 9000 /myapp. Set value to - to delete the param. Use param list /myapp to list the params.\nFor containerized apps, all params specified for the app (including ones not specified in params.star spec) are passed to the container at runtime as environment parameters. CL_APP_PATH is a special param passed to the container with the app installation path (without the domain name). PORT is also set with the value of the port number the app is expected to bind to within the container.","building-apps-from-spec#Building Apps from Spec":"A spec (specification) can be set for an app. This makes Clace use the spec as a template to specify the app configuration. Use app create --spec python-flask while creating an app or change the spec using app update-metadata spec python-flask /myapp. The spec brings in a set of predefined files. If a file with the same name is already present in the app code, then the spec file is ignored. So if the app code and spec both define a Containerfile, the file from the app code takes precedence. If the app folder contains just app.py\nflaskapp/app.pyfrom flask import Flask app = Flask(__name__) @app.route(\"/\") def hello_world(): return \"\u003cp\u003eHello, World!\u003c/p\u003e\" Creating an app like clace app create --approve --spec python-flask ./flaskapp /myapp will do everything required to fully define the Clace app. If the app has additional python dependencies, add a requirements.txt file in the app source code. By default, only the flask package is installed. The file in the app source takes precedence.\nSee https://github.com/claceio/appspecs for the list of specs. The Clace server build includes these spec by default. Additional specs can de defined by creating a folder $CL_HOME/config/appspecs. Any directory within that is treated as a spec. If the name matches with the predefined ones the spec in the config folder takes precedence. No server restart is required after spec changes. Setting up the server by doing\ncd $CL_HOME/config git clone https://github.com/claceio/appspecs.git ensures that the specs are updated to the latest version. Periodically doing a git pull on this folder refreshes the specs. Instead of cloning the main spec repo, a custom spec repo can also be used similarly. If no custom specs are defined, the specs as bundled in the Clace server build are available.","containerized-app#Containerized App":"A containerized apps needs to have a Containerfile (or Dockerfile) to define how the image is built. The app definition can have\napp.starload(\"proxy.in\", \"proxy\") load(\"container.in\", \"container\") app = ace.app(\"My App\", routes=[ ace.proxy(\"/\", proxy.config(container.URL)) ], container=container.config(container.AUTO), permissions=[ ace.permission(\"proxy.in\", \"config\", [container.URL]), ace.permission(\"container.in\", \"config\", [container.AUTO]) ] ) which completely specifies the app. This is saying that the app is using the container plugin to configure the container and the proxy plugin to proxy all API calls (/ route) to the container url. On the first API call to the app, Clace will build the image, start the container and proxy the API traffic to the appropriate port. No other configuration is required in Starlark. If the container spec does not define the port being exposed, then the container config needs to specify the port number to use. The port number can be parameterized.\nContainerized Apps has more details on building containerized apps.","more-examples#More examples":"There is one disk_usage example here and many in the apps repo. The disk_usage example uses the MVP classless library for styling and shows a basic hypermedia flow. The cowbull game has multiple pages, each page with some dynamic behavior. The cowbull game depends on another service for data persistence, so it is implementing a backend for frontend pattern. For styling, it uses the DaisyUI component library for Tailwind CSS. These two examples work fine with Javascript disabled in the browser, falling back to basic HTML without any HTMX extensions.\nThe memory_usage example uses the d3 library to show a interactive display of the memory usage for processes on the machine. The plot library is automatically imported as a ECMAScript module and the custom javascript code works with a JSON api on the backend. The default in Clace is hypermedia exchange, JSON can be used for data API’s.\nStylingStyling configuration for apps, using CSS and Tailwind JavascriptImporting JavaScript libraries and ESModules TemplatesHTML template handling details ","simple-text-app#Simple Text App":"The hello world app for Clace is an ~/myapp/app.star file containing:\napp.stardef handler(req): return \"hello world\" app = ace.app(\"hello\", routes = [ace.api(\"/\", type=ace.TEXT)] ) Run clace app create --auth=none ~/myapp /hello. After that, the app is available at /hello\n$ curl localhost:25222/hello hello world The default response type is ace.HTML. ace.TEXT and ace.JSON are the other options. The data returned by the handler function is converted to the type format specified in the API.","structure#Structure":"The structure of a Clace application is:\nOne Clace application per folder, static sub-folder contain static assets An app.star Starlark file, defining the application configuration Predefined builtins, accessed through the ace namespace A global called app, created using app = ace.app() call An optional default handler function called handler. Other handlers are referenced in the route config An optional error handler function called error_handler. Defining the error_handler enables automatic error handling An html template file called index.go.html if using custom layout If not using custom layout, an html template block called clace_body defined in any *.go.html file, for example app.go.html "},"title":"Developing Apps"},"/docs/develop/javascript/":{"data":{"":"Clace supports importing JavaScript libraries as JavaScript Modules . To use this feature, add\napp.star libraries=[ace.library(\"d3\", \"7.8.5\")] in the app definition. The fields in the ace.library structure are:\nProperty Optional Type Default Notes name false string The name of the library to import version false string The version of the library args true string[] [] Arguments to pass to esbuild The args array uses the esbuild cli syntax. For example, passing args as [\"--minify\"] will enable minification for the imported module.\nTo directly download a library from a CDN to the static folder, add the url directly. For example,\napp.star libraries=[\"https://unpkg.com/jquery@3.3.1/dist/jquery.min.js\"] The HTMX library and its SSE extension are automatically downloaded.","esbuild-config#Esbuild Config":"Clace includes esbuild, there is no need to install esbuild manually. The Clace server config has the entry\nclace.toml[system] node_path = \"\" The node_path property is used by esbuild, these paths are searched for packages in addition to the node_modules directories in all parent directories. See esbuild docs for details. Paths should be separated with : on Unix and ; on Windows.\nIf you install the npm packages in your home directory, esbuild will pick up those without any additional configuration. Since each Clace project is importing the npm package as a module, you do not need to maintain separate node_modules for each Clace project.\nIf you do not want esbuild to create modules, set the node_path property in the server config to disable. You will have to manually download the module file into the static folder.","javascript-modules#JavaScript Modules":"JavaScript modules (also called ESM or ECMAScript Modules) are a way to import Javascript libraries dynamically, providing a unique namespace for all functions. Modules, once imported, can be used in the client code without requiring any build steps. See jvns.ca and simonwillison.net for notes about this approach. Clace tries to provide an easy interface to modules, converting npm packages to module format so that subsequent code changes do not require any build steps.","notes#Notes":" The version number specified in the ace.library is used to create the file name under static/gen/esm. The actual package version depends on what was install using npm. Ensure that the same version is installed by npm as specified in the library config. Only the minify option for esbuild has been tested with Clace. Other options like chunking the files might not work currently. The JavaScript support is for running JS on the browser. There is no support for running JavaScript on the Clace server, the server runs only Go code and starlark applications. If an library url is specified, that is downloaded directly. There is no need for npm package in that case. ","workflow#Workflow":"The workflow when using modules in Clace for an app in dev mode is:\nEnsure nodejs is installed Using npm, install package you want to use a modules. for example npm install d3 Add the ace.library entry in the app config. Clace will automatically run esbuild and import the package as a module into static/gen/esm Add an importmap in the head section of the html. Like here. \u003cscript type=\"importmap\"\u003e { \"imports\": { \"d3\": \"{{ static \"gen/esm/d3-7.8.5.js\"}}\" } } \u003c/script\u003e Use the library as required in your client. Like here import * as d3 from \"d3\"; Creating the module is a one time step. The generated module can be checked into source code. On a new machine, to make code changes to the app, you do not need nodejs or npm to be installed.\nFor production deployment, no changes are required. Checkout the git repo containing the source code and create a Clace app. Clace will serve the static assets over HTTP/2 with content hash based caching. The assets are compressed for serving, there is no need usually for mimifying the modules."},"title":"JavaScript"},"/docs/develop/styling/":{"data":{"":"Clace supports working with Classless CSS libraries and also with TailwindCSS and DaisyUI. To use this, add the directive\napp.star style=ace.style(\"daisyui\") in the app definition. The fields in the ace.style structure are:\nProperty Optional Type Default Notes library false string The library to use, url to classless library, “tailwindcss” or “daisyui” themes true string[] [] The daisyui themes to include disable_watcher true bool false Whether to disable the tailwind watcher process startup in dev mode light true string emerald The DaisyUI theme to use in light mode for Actions dark true string night The DaisyUI theme to use in dark mode for Actions ","classless-css#Classless CSS":"If the library property is a url, it should point to a publicly accessible style file. The style file is downloaded into the static/gen/css/style.css file. The file is automatically included as part of the clace_gen_import template.\nFor example,\napp.star style=ace.style(\"https://unpkg.com/mvp.css@1.14.0/mvp.css\"), imports the MVP.css library. Since this is classless, no changes are required in the HTML templates.","daisyui#DaisyUI":"To use DaisyUI, in app settings, add\napp.star style=ace.style(\"daisyui\", themes=[\"dark\"]) Change to the preferred theme. DaisyUI is a good option to use to get great default styling for components, with the full flexibility of Tailwind. To use DaisyUI, use the npm version of Tailwind or use this custom version of the standalone CLI with DaisyUI included. Clace takes care of creating the config files. Using the CDN version of DaisyUI or Tailwind is not recommended since that will cause the style files to be large.\nIf using Actions, DaisyUI styles are automatically included. The themes can be customized using the light and dark property.","tailwindcss#TailwindCSS":"To use TailwindCSS, in app settings, add\napp.star style=ace.style(\"tailwindcss\") Tailwind CSS works by scanning the HTML files for class names, generating the corresponding styles and then writing them to a static CSS file. A watcher process is started when an app using Tailwind is loaded in dev mode. The output of the watcher is written to static/gen/css/style.css file. This file is automatically included as part of the clace_gen_import template.\nTo ensure that the tailwind watcher is started, the tailwind CLI needs to be installed manually. The standalone CLI can be used. If using DaisyUI, use this custom build of the standalone CLI with DaisyUI included.\nThe Clace server config file has the following entries:\nclace.toml[system] tailwindcss_command = \"npx tailwindcss\" file_watcher_debounce_millis = 300 tailwindcss_command is the command use to start the watcher. If the standalone version is being used change to\nclace.toml[system] tailwindcss_command = \"/path/to/tailwindcss\" file_watcher_debounce_millis is used to prevent repeated reloads of the application files during dev mode. On slower machine, this value might have to be increased, but setting it too high will cause the reload to be slower."},"title":"Styling"},"/docs/develop/templates/":{"data":{"":"Clace uses Go HTML templates for returning data to the client. See here for an overview of the template syntax. Hugo docs are a good source for an overview of using go templates.\nThe Sprig template library functions are included automatically. Two functions from Sprig which are excluded for security considerations are env and expandenv.\nTwo extra functions static and fileNonEmpty are added for handling static file paths.","app-layout#App Layout":"When using custom layout with custom_layout=True, the app developer has to create the index.go.html file. Add a directive like:\n{{ template \"clace_gen_import\" . }} in the head section to ensure that the auto generated clace_gen_import directives are loaded in the . This will include the style files, HTMX library and the live reload functionality in dev mode.\nIn the default layout mode, the auto generated index_gen.go.html file is used. The app developer has to provide a clace_body block. It can be in any template file, the convention is to use app.go.html. For example:\n{{block \"clace_body\" .}} Data is {{.Data}} {{end}} The .Data binding has the data as returned by the handler function for the route.","filenonempty-function#fileNonEmpty function":"The fileNonEmpty function returns a bool, indicating whether a static file with that non-hashed name is present and is not empty. This can be used to conditionally include style files if present.\nFor example\n{{ if fileNonEmpty \"css/style.css\" }} \u003clink rel=\"stylesheet\" href=\"{{ static \"css/style.css\" }}\" /\u003e {{ end }} checks if the “css/style.css” file is present and not empty. If so, it is linked using the static function, which returns a hashed file name which can be cached aggressively.\n⚠️ The path passed to static and fileNonEmpty functions should not include static, it is automatically added. So use {{ static \"css/style.css\" }}, not {{ static \"static/css/style.css\" }} ","static-function#static function":"This function takes a file name and returns the url for a file in the static folder with a sha256 hash included in the file name. This approach is similar to the hashfs library. If the static folder contains a file file1 with the content file1data, then a call to static \"file\" will return /test/static/file1-ca9e40772ef9119c13100a8258bc38a665a0a1976bf81c96e69a353b6605f5a7, assuming the app is installed at /test.\nThe returned file name has a hash based on the file contents. The file server used by Clace will serve aggressive cache headers Cache-Control: public, max-age=31536000 when this file is referenced by the browser. When the file contents change, the content hash will change and the file name will change. The files on disk are not renamed, only the filesystem used by the Clace server in memory sees the hashed file names.\nThis approach allows for a build-less system with aggressive static asset caching. The usual approach for this requires the static file to be renamed to have the hash value in the file name on disk. This require a build step to do the file renaming. The hashfs approach can avoid the build step. The file hash computation and compression are done once, during app installation in prod mode. There is no runtime penalty for this. In dev mode, the file hashing is done during the api serving.","static-root-files#Static Root Files":"The static folder is used for file which are served under the /static path. Content based hashing is supported for these files.\nFor files which need to be served under the root level, the static_root folder is used. Files in this folder are served at the root path. For example, if an app is installed at example.com: and a robots.txt file needs to be served, a file static_root/robots.txt can be added to the app. This will be automatically served at example.com/robots.txt. Note that the static folder path is stripped from the route name. The file name should not conflict with any of the API routes defined in the app. Nested folders are looked up in the static root folder.\nContent based hashing is not supported for static root files. These files are expected to be used for well known files like favicon.ico. Most regular static file serving use cases should use the static folder, not the static_root folder.","structured-template-layout#Structured Template Layout":"The default in Clace is to load all the templates in one parse operation. This is easy to get started with but can result in challenges when the same template block needs to be duplicated in different files. Clace also supports a structured template layout. See this blog for an explanation about the differences between the two layouts. The default in Clace is the WordPress layout, all template files are loaded in one go. To use the second, Django layout, use the structured format.\nIf there is a base_templates folder in the app main folder with one or more *.go.html files, the structured template layout is used. In the structured layout format, all the base template files are loaded in one parse operation. Each of the files in the app main folder is then individually loaded. Each top level file has access to its own template blocks plus the base templates.\nThis has the advantage that the main templates can have duplicate templates, with no conflicts because they are loaded individually. For example, if there is a base_templates/base.go.html file with\n\u003chtml\u003e \u003chead\u003e\u003c/head\u003e {{block \"body\" .}} {{end}} \u003cfooter\u003e\u003c/footer\u003e \u003c/html\u003e {{end}} and a index.go.html file with\n{{define \"body\"}} My Index Body {{end}} {{- template \"full\" . -}} and a help.go.html file with\n{{define \"body\"}} My Help Body {{end}} {{- template \"full\" . -}} then a route using index.go.html will get the HTML for the index page and route using help.go.html with get HTML help page. Although the body is defined in two template files, there is no conflict since the root level template files are loaded independently.\nWithout structured template layout, if a duplicate block is found, the one to be used depends on the order in which the files are loaded. To change the folders used for base templates, set:\nsettings={ \"routing\": {\"base_templates\": [\"base_templates\", \"template_helpers\"]} } ","template-file-location#Template File Location":"Templates are loaded once on app initialization. In dev mode, they are automatically reload on file updates. By default, the app source home directory is searched for template files. This can be changed by adding this directive in the ace.app config.\nsettings={ \"routing\": {\"template_locations\": [\"*.go.html\", \"templates/*.go.html\"]} } the default is [\"*.go.html\"]. If additional directories are added, \"*.go.html\" still needs to present in the list since generated files are created in the app home directory. Also, all folders in the list need to contains at least one template file. File names have to be unique across folders. Files are referenced by their name, without the folder name, when used in template import directives."},"title":"Templates"},"/docs/installation/":{"data":{"":"","initial-configuration#Initial Configuration":"To use the clace service, you need an initial config file with the service password and a work directory. Create the clace.toml file, and create a randomly generate password for the admin user account\nclace password \u003e $CL_HOME/clace.toml This will print a random password on the screen, note that down as the password to use for accessing the applications.","install-from-source#Install from Source":"To install from source\nEnsure that a recent version of Go is available, version 1.21.0 or newer. Checkout the Clace repo. The below instructions assume you are using $HOME/clhome/clace.toml as the config file and $HOME/clhome as the work directory location. First add the below env variables to your shell .profile or .bash_profile:\nexport CL_HOME=$HOME/clhome export PATH=$CL_HOME/bin/:$PATH Source the update profile file, like source ~/.bash_profile. Build the Clace binary\n# Ensure go is in the $PATH mkdir -p $CL_HOME/bin mkdir $HOME/clace_source \u0026\u0026 cd $HOME/clace_source git clone -b main https://github.com/claceio/clace \u0026\u0026 cd clace go build -o $CL_HOME/bin/clace ./cmd/clace/ ","install-release-build#Install Release Build":"To install the latest release build on Linux, OSX or Windows with WSL, run the install script. Note down the password printed. Add the env variables as prompted and then start the service.\ncurl -L https://clace.io/install.sh | sh source $HOME/clhome/bin/clace.env clace server start \u0026\u0026 sleep 2 clace app create --approve github.com/claceio/clace/apps/system/disk_usage /disk_usage Clace is installed under $HOME/clhome by default. The disk usage app should be available at https://127.0.0.1:25223/disk_usage after allowing the self-signed certificate. admin is the username and use the password printed by the install script.\nOn Windows, to install the Clace application, run\npwsh -Command \"iwr https://clace.io/install.ps1 -useb | iex\" Use powershell if pwsh in not available. The app is installed under $HOME\\clhome by default. Note down the generated password for the admin user. Open a new command window (to get the updated ENV values) and run\nclace server start to start the server. In another window, apps can be installed using same command as Linux/OSX. To install a bookmark manager app, run\nclace app create --approve github.com/claceio/apps/utils/bookmarks /book The bookmark manager app should be available at https://127.0.0.1:25223/book.\nSee start the service for details.","load-an-app#Load an App":"To create an app, ensure that code is available locally and then run the Clace client\nclace app create --dev $HOME/clace_source/clace/examples/disk_usage /disk_usage To audit and approve the app’s security policies, run\nclace app approve /disk_usage This will create an app at /disk_usage with the example disk_usage app. The disk_usage app provides a web interface for the du command, allowing the user to explore the subfolders which are consuming most disk space.\nTo access the app, go to https://127.0.0.1:25223/disk_usage. Use admin as the username and use the password previously generated. Allow the browser to connect to the self-signed certificate page. Or connect to http://127.0.0.1:25222/disk_usage to avoid the certificate related warning.\nThe code for the disk usage app is in GitHub. app.star is the starlark config and app.go.html is the html template. The other files are generated files and are created by Clace during app development.","start-the-service#Start the service":"To start the Clace server, run\nclace server start The service logs will be going to $CL_HOME/logs. The service will be started on https://localhost:25223 by default."},"title":"Installation"},"/docs/plugins/":{"data":{"":"Overview of Clace plugins and how to use them\nOverviewOverview about Clace plugins, automatic error handling and accounts CatalogList of plugin and their APIs: exec, fs, http Store PluginStore plugin for persisting data to a database Container PluginContainer plugin supports configuring container backend Proxy PluginProxy plugin supports proxying API calls "},"title":"Plugins"},"/docs/plugins/catalog/":{"data":{"":"The page lists the available plugins and their API details.","container-config#Container Config":"The container.in plugin supports configuring for using a container for deploying backend APIs. See container for details.","database-storage#Database Storage":"The store.in plugin supports a document store interface for writing data to SQLite. See store for details.","exec-plugin#Exec Plugin":"run The exec.in plugin allows running external commands, starting a new process for the specified command. The APIs available are:\nAPI Type Notes run Read/Write Runs the command as a new process The API supports the following parameters:\npath (string, required) : the command to run args (list of strings, optional) : arguments to pass to the cmd env (list of strings, optional) : the env to pass to the cmd, in the form key=value process_partial (bool, optional) : whether to process the output when there is a failure stdout_file (bool, optional) : whether to send the stdout for the process to a temporary file on disk The default response (when stdout_file is False) for the exec API (value within plugin_response) is of type list of strings. The stdout is scanned and split on newlines. The list of lines is returned. For example\napp.star ret = exec.run(\"ls\", [\"-l\", \"/\"], process_partial=True) if ret.error: return {\"Error\": ret.error} for line in ret.value: # Process lines ⚠️ Note: Only first 100MB of the command stdout output is scanned currently, the rest is discarded. If stdout_file is True, then the plugin output value is the name for the temp file to which the output was written. There is no size limit on this file output. The user is responsible for deleting this file.","fs-plugin#FS Plugin":"The fs.in allows working with local file system. The APIs available are\nAPI Type Notes abs Read Returns the absolute path for given relative path list Read List files in specified directory find Read Find files under specified directory matching criteria serve_tmp_file Read Load file metadata to the Clace database and make available through API abs The abs API supports the following parameter:\npath (string, required) : the file path The response for the API (value within plugin_response) is of type string, the absolute path for given path.\nlist The list API supports the following parameters:\npath (string, required) : the directory path recursive_size (bool, optional, default false) : whether to include the recursive size of sub-directories ignore_errors (bool, optional, default false) : whether to ignore errors when accessing entries The response for the API is a list of type FileInfo. The FileInfo struct contains the fields:\nname (string) : the file name size (int) : the file size in bytes, rounded up to 4K is_dir (bool) : is it a directory mode (int) : file mode info find The find API supports the following parameters:\npath (string, required) : the directory path name (string, optional) : the file name glob pattern to match limit (int, optional, default 10K, max 100K) : the limit on number of entries to return min_size (int, optional) : the minimum file size in bytes to look for ignore_errors (bool, optional, default false) : whether to ignore errors when accessing entries The response for the find API is a list of type FileInfo, same as returned by list.\nserve_tmp_file The serve_tmp_file API supports the following parameters:\npath (string, required) : the file path to serve name (string, optional, default basename of path) : the file name to use (when serving the API) visibility (string, optional, default fs.USER) : the API access level for the file (fs.USER or fs.APP) mime_type (string, optional, default application/octet-stream) : the mime type to use when serving the file expiry_minutes (int, optional, default 60 minutes) : how long to keep the file metadata, set to zero for no deletion single_access (bool, optional, default True) : whether to serve the file just once and then automatically delete it The response for the serve_tmp_file API is a dict with the fields:\nid (string) : the id of the metadata entry url (string) : the url path (without domain) for downloading the file name (string) : the file name The serve_tmp_file API creates a metadata entry in the Clace database. The file can be served through an API using this entry. The default behavior is the file is accessible only to the user who created it. First download of the file will serve the file and automatically deletes the file. The file is deleted after 60 minutes in case there is no API access before that. Deleting the metadata entry removes the database entry and also deletes the file from disk. If the file should not be deleted, do:\nret = fs.serve_tmp_file(\"/tmp/myfile\", single_access=False, expiry_minutes=0)\nSee number_lines app code:demo for an example of using this API. Setting visibility to fs.APP will make the API available to anyone who has access to the app.","http-plugin#HTTP Plugin":"get,head,options,post,put,delete,patch The http.in plugin supports making HTTP API calls. The APIs available are:\nAPI Type Notes get Read HTTP Get request head Read HTTP Head request options Read HTTP Options request post Write HTTP Post request put Write HTTP Put request delete Write HTTP Delete request patch Write HTTP Patch request All the API’s support the following parameters:\nurl (string, required) : the url to send the request to params (dict, optional) : url params to send headers (dict, optional) : HTTP headers to send body (string, optional) : body to send form_body (dict, optional) : form body to send form_encoding (string, optional) : the form encoding to use, application/x-www-form-urlencoded (default) or multipart/form-data json_body (object, optional) : the object to send as json encoded body auth_basic (tuple(string, string), optional): HTTP basic auth username and password The response for all API’s (value within plugin_response) contains following properties:\nurl (string): the url the response is for status_code (int): the HTTP status code headers (dict): the output headers encoding (string): the transfer encoding header body() (string) : the response body as a string json() (object) : the response body un-marshalled as a json If the API calls fails to go through then the plugin response error property will be set. If the API goes through, then the response error will not be set, even if API call fails with an HTTP error. The status_code will indicate whether the API succeeded on the server. To handle all possible error conditions, do (change to handle all 2xx codes if required)\napp.starret = http.get(\"http://localhost:9999/test\") if ret.error or ret.value.status_code != 200: return # error handling val = ret.value.json() # success handling ","proxy-config#Proxy Config":"The proxy.in plugin supports proxying API calls to deploying backend APIs. See proxy for details."},"title":"Plugin Catalog"},"/docs/plugins/container/":{"data":{"":"The container.in plugin provides the config API to allow configuring the container backend.","api#API":"The container.in plugin has just one api, config\nAPI Type Notes config Read Configures the container details for the app The config API supports the following parameter:\nsrc (string, optional) : the source for containerfile, auto by default port (int, optional) : the port number exposed from the container scheme (string, optional) : the url scheme, http by default health (string, optional) : the health check API, / by default lifetime (string, optional) : the lifetime for the container, currently unused build_dir (string, optional) : the build directory for the build, / by default When the src is auto, the container file is auto detected. It checks for presence of either Containerfile or Dockerfile. If the value begins with image:, the subsequent portion is treated as the image to download. No image build is done in that case. Any other value for src is treated as the file name to use as the container file.\nport can be specified in the container file, using a EXPOSE directive. If a value other than zero is specified in the config, that takes precedence over the value in the Expose.\nA sample program using the container config is\napp.starload(\"proxy.in\", \"proxy\") load(\"container.in\", \"container\") app = ace.app(\"My App\", routes=[ ace.proxy(\"/\", proxy.config(container.URL)) ], container=container.config(container.AUTO), permissions=[ ace.permission(\"proxy.in\", \"config\", [container.URL]), ace.permission(\"container.in\", \"config\", [container.AUTO]) ] ) ","introduction#Introduction":"Clace can build and manage containers for implementing the app backend APIs. The config API is used to configure at the app level what configuration is used for the container."},"title":"Container Plugin"},"/docs/plugins/overview/":{"data":{"":"Plugins provide an API for Clace Starlark code to call out to external systems. Plugins are implemented in Go. Every plugin API calls needs to be in the approved list for it to be permitted. See security for an overview of the security model.\nEach plugin is identified by a unique name, like store.in or exec.in. Plugins ending with .in are internal plugins, built into the Clace binary. Support for external plugins which are loaded dynamically is planned.","automatic-error-handling#Automatic Error Handling":"Clace supports automatic error handling, so that the handler functions do not have to check the error status of every plugin API call. The way this is implemented is such that if no explicit error handling is done, then the automatic error handling kicks in. If explicit error handling is done, then automatic error handling is not done. See bookmarks app for an example of how the automatic error handling can be used.\nIf the error_handler function is defined, then that is called with the error. The manual error checking works the same as mentioned above. But if no manual error checking is done, then the Clace platform will automatically call the error_handler function in case of an error. The error_handler could be defined as:\napp.stardef error_handler(req, ret): if req.IsPartial: return ace.response(ret, \"error\", retarget=\"#error_div\", reswap=\"innerHTML\") else: return ace.response(ret, \"error.go.html\") When no explicit error checks are done, the automatic error handling happens in these three cases:\nValue Access When the response value is accessed Next API call When the next plugin API call happens (to any plugin function) Handler Return When the handler function returns Value Access If the handler code is\napp.star ret = http.get(\"https://localhost:9999/test\") print(ret.value.json()) If the get API had succeeded, then the value property access will work as usual. But if the get API had failed, then the value access will fail and the error_handler will be called with the original request and the error response.\nNext API Call If the value is not being accessed, then the next plugin call will raise the error. For example, if the handler code is\napp.star store.begin() bookmark = store.select_one(table.bookmark, {\"url\": url}).value The response of the begin API is not checked. When the next select_one API is called, if the previous begin had failed, the select_one API will raise the previous API call’s error, the select_one will not run.\nHandler Return If the handler code is\napp.star def insert(req): store.begin() book = doc.bookmark(\"abc\", []) store.insert(table.bookmark, book) store.commit() Assume all the API calls had succeeded and then the commit fails. Since the value is not accessed and there is no plugin API call after the commit call, the Clace platform will raise the error after the handler completes since the commit had failed.\nOverriding Automatic Error Handling The automatic error handling is great for handling the unusual error scenarios. For the error scenarios which are common, like a database uniqueness check failure, the error handing can be done explicitly in the handler code. If the handler code is\napp.starret = store.insert(table.bookmark, new_bookmark) if ret.error: return ace.response(ret, \"error.go.html\") The automatic error handling will not be invoked in this case since the ret.error is being checked. Checking the truth status of ret also will disable the automatic error handling:\napp.starret = store.insert(table.bookmark, new_bookmark) if not error: return ace.response(ret, \"error.go.html\") ℹ️ Note: When developing a new app, first define the error_handler and test it for the partial and full page scenarios. All subsequent handler code does not need to handle errors unless specific handling is required. If no error_handler is defined, a generic error message screen is returned. If is recommended to define a custom error_handler. ","plugin-accounts#Plugin Accounts":"Some plugins like exec.in do not require any account information. Others like store.in need some account information. The account configuration for a plugin is loaded from the Clace config file clace.toml. For example, the default configuration for store.in is here, which contains:\nclace.toml[plugin.\"store.in\"] db_connection = \"sqlite:$CL_HOME/clace_app.db\" Any application using the store.in plugin will by default use the $CL_HOME/clace_app.db sqlite database. To change the default account config used by apps, update clace.toml and restart the Clace server. For example, adding the below will overwrite the default store.in config for all apps.\nclace.toml[plugin.\"store.in\"] db_connection = \"sqlite:/tmp/clace_app.db\" Account Linking If specific account config is required for an app, then the app can be linked to a specific account config. First add a new account config by adding in clace.toml\nclace.toml[plugin.\"store.in#tmpaccount\"] db_connection = \"sqlite:/tmp/clace_app.db\" For an app /myapp using store.in, run clace account link --promote /myapp store.in tmpaccount\nThis links the myapp app to use the tmpaccount account.\nNamed Account In addition to using account linking, the plugin code itself can point to specific accounts. For example, if the app code has\napp.starload(\"http.in#google\", \"googlehttp\") then the app will use the http.in#google account config by default. This also can be overridden using account links, by running clace account link --promote /myapp http.in#google myaccount\nThis approach is useful if an app has to access multiple accounts for the same plugin. The account linking approach is recommended for normal scenarios.\nClace apps aim to be portable across installations, without requiring code changes. Using account config allows the app code to be independent of the installation specific account config.","plugin-usage#Plugin Usage":"To use a plugin, load it using\napp.starload(\"http.in\", \"http\") This adds http to the namespace for the app. To make a call to the plugin, first add the permissions to the app config.\napp.star permissions=[ ace.permission(\"http.in\", \"get\"), ace.permission(\"http.in\", \"post\") ], Run clace app approve /myapp to authorize the app to call the get and post methods on the http plugin.\nIn the app handler code, do\napp.star ret = http.get(SERVICE_URL + \"/api/challenge/\" + challenge_id) if not ret: return ace.response(ret.error, \"invalid_challenge_id\", code=404) At runtime, Clace will check if the get call is authorized. If so, the call to the plugin will be performed.","response-handling#Response Handling":"All plugin API calls return a plugin_response structure. The fields in this are\nerror The error message string, empty string if no error error_code The error code integer, zero if no error value The actual return value for the plugin API call. The datatype for this depends on the API, check the API documentation for details. To check the error status of an API call:\nCheck boolean value for the return. If false, that indicates an error which can be returned. If no error, get the value property and continue with processing For example,\napp.star ret = http.get(\"https://localhost:9999/test\") if not ret: # error condition return ace.response(ret, \"error_block\") # success print(ret.value.json()) # ret.value is the return value. The http plugin response has a json() function An alternate way to write the error check is\napp.star ret = http.get(\"https://localhost:9999/test\") if ret.error: # error condition return ace.response(ret, \"error_block\") # Success print(ret.value.json()) "},"title":"Plugin Overview"},"/docs/plugins/proxy/":{"data":{"":"The proxy.in plugin provides the config API to allow proxying of API calls.","api#API":"The proxy.in plugin has just one api, config\nAPI Type Notes config Read Configures the proxy details for the route The config API supports the following parameter:\nurl (string, required) : The url to proxy to. Use container.URL to proxy to backend container strip_path (string, optional) : extra path values to strip from the proxied API call preserve_host (bool, optional) : whether to preserve the Host header. Default false, the Host header is set to the target host value strip_app (bool, optional) : whether to strip the app path from the proxied API call. Default true. ","example#Example":"This is an example app which proxies data to google.com. This app has to be installed at the root level, since google does not use relative paths.\napp.starload(\"proxy.in\", \"proxy\") app = ace.app(\"Proxy App\", routes=[ ace.proxy(\"/\", proxy.config(\"https://www.google.com\")) ], permissions=[ ace.permission(\"proxy.in\", \"config\", [param.url]), ] ) ","introduction#Introduction":"Clace can proxy API calls to external endpoints or to backend APIs implemented in a container. The config API is used to configure at the route level what configuration is used for the proxy."},"title":"Proxy Plugin"},"/docs/plugins/store/":{"data":{"":"The store.in plugin provides a document store interface to work with SQLite tables (PostgreSQL support is coming soon). The goal for the store plugin is to support a full managed interface, creating the app automatically creates the tables required for the app.","automatic-fields#Automatic Fields":"All tables have some fields added automatically. These are:\nField Type Notes _id int Primary key _version int Schema version _created_by string User id _updated_by string User id _created_at timestamp _updated_at timestamp These fields can be accessed like regular user defined field in the store APIs. So bookmark._id can be used the way bookmark.url is used in all the APIs.","filter#Filter":"The select, select_one, count and delete APIs take a filter parameter. The filter has to be specified as a dict. The format of the filter is similar to the format used by MongoDB. The advantage of this over a SQL expression is that there is no possibility of SQL injection, even with an improperly written application.\nThe filter is specified as a list diction, the keys are the names of the field to apply the condition on. The value can be a value, in which case it is treated as a equality match. If the value is an diction, then the it is treated as a expression to apply on the specified field.\nFor example, a filter {\"age\": 30} is equivalent to sql where clause age = ? with the parameter bound to 30. Filter {\"age\": 30, \"city\": \"New York\", \"state\": \"California\"} is same as sql age = ? AND city = ? AND state = ?, with the appropriate bindings. To express an or condition, do filter as {\"age\": 30, \"$or\": [{\"city\": \"New York\"}, {\"state\": \"California\"}]}. That translates to age = ? AND ( city = ? OR state = ? )\nTo express an inequality condition, do {\"age\": {\"$gt\": 30}} which becomes age \u003e ?.\nThe logical operators supported are $AND and $OR, case insensitive.\nThe filter operators supported (case insensitive) are\nFilter SQL Notes $GT \u003e $LT \u003c $GTE \u003e= $LTE \u003c= $EQ = Default when value is not a dict $NE != $LIKE like Value has to be passed with % added, it is not added automatically. For example \"%test%\" ","introduction#Introduction":"The store.in plugins automatically creates tables with the specified schema. The tables are created on first load unless they are already present. The tables are linked to the app. The tables use a document store interface. The data is stored as JSON(B) data types. To query the data, a structured interface is used similar to the one provided by MongoDB. The advantage of this approach is that SQL injection is not possible, even if the application code is incorrectly written.","iterators#Iterators":"The select API returns a document iterator. Use a regular python for loop to iterate on the entries. For example,\napp.starret = store.select(table.bookmark, {}, limit=100, sort=[\"_created_at:desc\"]) if ret.error: return ace.response({\"error\": ret.error}, \"error\") bookmarks = [] for row in ret.value: bookmarks.append(row) Iterating till the end of the loop automatically closes the iterator. Returning from a handler without closing an iterator will cause the handler to fail. The iterator is automatically closed by the Clace platform to prevent a resource leak. The API failure is used to indicate to the developer that the code needs to be fixed to explicitly close the iterator.\n⚠️ Note: The iterator cannot be directly returned from the handler. A list needs to be created and populated if the entries need to be passed to the template. ","schema-definition#Schema Definition":"The schema for the app is specified in the schema.star file in the root directory of the app code. The format of this file is like:\napp.startype(\"bookmark\", fields=[ field(\"url\", STRING), field(\"tags\", LIST), ], indexes=[ index([\"url\"], unique=True) ]) type(\"tag\", fields=[ field(\"tag\", STRING), field(\"urls\", LIST), ], indexes=[ index([\"tag\"], unique=True) ]) Multiple types can be specified. Each type has a name, list of fields and list of indexes. Each field has a name and a type, the valid types are INT, STRING, BOOLEAN, LIST and DICT.\nEach type maps to one table in the underlying database. Indexes can be created on the fields. Each index is specified as list of field names. Adding :desc to the field name changes the index to be sorted descending instead of default ascending. Setting unique property to True makes it an unique index.","schema-design#Schema Design":"SQL tables are used as underlying storage, but joins are not supported by the store.in interface. The schema design to use would be same as schema used for a document database. Since LIST and DICT data types are supported, de-normalized schema is recommended instead of normalized schema.","select-limits-and-sort#Select Limits and Sort":"For the select API, a limit of 10,000 is set as the default limit value. The API can pass a different limit value if required. The maximum value allowed for the limit is 100,000. Passing a limit beyond that will result in an API failure.\nThe sort argument can be used to sort the result for the select API. The argument is a list of strings. For example, [\"age\", \"city\"] is sorted on age and city ascending. [\"age\", \"city:desc\"] is sorted on age ascending and city descending.","store-apis#Store APIs":"The API’s in the store.in plugin and their arguments and response are:\nAPI Type Args Response Value Notes begin Read - - Begin a new transaction commit Write - - Commit active transaction rollback Read - - Rollback active transaction select_by_id Read table: string id : int doc Select one record by id select Read table: string filter : dict sort : list string offset : int limit : int (default 10,000) doc iterator Select by filter select_one Read table: string filter : dict doc Select one by filter count Read table: string filter : dict int Count entries by filter insert Write table: string entry : doc id : int Insert a document update Write table: string entry : doc count : int Update a document delete_by_id Write table: string id : int count : int Delete one document by id delete Write table: string filter : dict count : int Delete multiple docs by filter ","store-types#Store Types":"The type information is read from the schema file and schema types are automatically created for the app. The doc namespace has type objects for each type. For the previous example, the two types created are doc.bookmark and doc.tag. The table namespace also has entry populated which reference the table names for the type. For the previous example, the two table names available are table.bookmark and table.tag. This allows creating objects and persisting them using the store API by doing:\napp.starbookmark = doc.bookmark( url=\"http://clace.io\", tags=[\"webapps\", \"tools\"]) ret = store.insert(table.bookmark, bookmark) ","transactions#Transactions":"The transaction handling APIs begin, commit and rollback take no arguments. All the other APIs take the table name as the first argument. The transaction created by the begin is saved in a thread local, there is no need to pass the transaction manually to subsequent API calls. The transaction rollback is automatically done at the end of the API handler if commit is not done explicitly."},"title":"Store Plugin"},"/docs/quickstart/":{"data":{"":"Clace is an Apache-2.0 licensed project building a web app development and deployment platform for internal tools. This page provides an overview of how to start with Clace and provides links to doc pages with more details.","action-apps#Action Apps":"For use cases where an existing CLI application or API needs to be exposed as a web app, actions provide an easy solution. First, define the parameters to be exposed in the form UI. Create a params.star file with the params. For example,\nparams.starparam(\"dir\", description=\"The directory to list files from\", default=\"/tmp\") The app defines a run handler which runs ls on the specified directory. The output text is returned.\napp.starload (\"exec.in\", \"exec\") def run(dry_run, args): out = exec.run(\"ls\", [\"-Lla\"]) if out.error: return ace.result(out.error) return ace.result(\"File listing for \" + args.dir, out.value) app = ace.app(\"List Files\", actions=[ace.action(\"List Files\", \"/\", run, description=\"Show the ls -a output for specified directory\")], permissions=[ ace.permission(\"exec.in\", \"run\", [\"ls\"]), ], ) The app, when accessed will look as shown below, with the ls command output displayed:\nSee list files code:demo for the above app. See dictionary code:demo for another actions example app which shows different type of reports. Actions has more details on building app actions.","app-installation#App Installation":"To install apps, run clace app create --approve \u003csource_url\u003e \u003c[domain:]app_path\u003e. For example,\nclace app create --approve github.com/claceio/apps/system/disk_usage /disk_usage This is installing the system/disk_usage app from the main branch of the claceio/apps repo on GitHub. The app is installed for the default domain, to the /disk_usage path. Opening https://127.0.0.1:25223/disk_usage will initialize the app and show the app home page.\n⚠️ The /disk_usage/* path is now reserved for API’s under this app. No new apps can be installed under the /disk_usage/ path, but /disk_usage2 is available. Similarly, installing an app under / path means no new apps can be installed for the default domain. If the app code is available on the Clace server node, the app create can be done directly with the local disk path:\nclace app create --approve ./diskapp /disk_usage_local When developing an app, the source code for the app has to be present locally. To install an app in dev mode, add the --dev option.\nclace app create --dev --approve ./diskapp /disk_usage_dev In dev mode, source code changes are picked up immediately and the app is live reloaded. For non-dev (prod) apps, app reload has to be done to pick up changes, from local disk or from git.\nclace app reload --approve --promote \"/disk_usage*\" For apps created from GitHub source, app reload will pick up the latest changes from the branch specified during app create (default is main). For apps created from local disk sources, the reload loads from the folder originally used during the create. For non-dev apps, the source code is loaded into the SQLite metadata database managed by the Clace server.This allow for versioning, even when working with local sources.","app-listing#App Listing":"Use clace app list to get list of installed app. By default, all apps are listed. Use a glob pattern like example.com:** to list specific apps. Pass the --internal or -i option to list to include the internal apps in the app listing. The pattern matches the main apps, and if the internal option is specified, the matched app’s linked apps are also listed.\nUse clace version list to get list of versions for an app. clace version switch allows switching between versions. The version command can be run separately on the staging app and prod app, like clace version list /myapp_cl_stage and clace version list /myapp. The current version is indicated in the output.\n$ clace version list /dugit Active Version Previous CreateTime GitCommit GitMessage 1 0 2024-02-16 19:39:05 +0000 UTC 03ccaa35927667977646 Added version file listing support 2 1 2024-02-16 19:55:51 +0000 UTC 03ccaa35927667977646 Added version file listing support =====\u003e 3 2 2024-02-16 21:18:16 +0000 UTC c00d7b1e99712de13745 Added version switching support $ clace version list /dugit_cl_stage Active Version Previous CreateTime GitCommit GitMessage 1 0 2024-02-16 19:39:05 +0000 UTC 03ccaa35927667977646 Added version file listing support 2 1 2024-02-16 19:54:22 +0000 UTC 03ccaa35927667977646 Added version file listing support 3 2 2024-02-16 20:38:44 +0000 UTC c00d7b1e99712de13745 Added version switching support =====\u003e 4 3 2024-02-16 21:18:42 +0000 UTC c00d7b1e99712de13745 Added version switching support $ clace app list -i /dugit Id Type Version Auth GitInfo Domain:Path SourceUrl app_prd_2cSkPeHiATfH46pcUX8EdZqdWQb PROD* 3 SYST main:c00d7b1e99712de13745 /dugit github.com/claceio/clace/examples/disk_usage app_stg_2cSkPeHiATfH46pcUX8EdZqdWQb STG 4 SYST main:c00d7b1e99712de13745 /dugit_cl_stage github.com/claceio/clace/examples/disk_usage In the above listing, the staging app is on version 4, prod app on version 3. The * in the app list output indicates that the prod app has staged changes waiting to be promoted. Running clace app promote /dugit will update prod with the staged changes. version revert reverts to previous version. version switch can be used to switch to particular version, next and previous are shortcuts for version numbers. Version commands run against the specific app, so revert can be done on the staging app or the main app independently.","app-security#App Security":"Application config is specified in Starlark code in the app.star file. By default, the app does not have any permissions. All external actions an app can perform are done through plugin API calls. Every plugin API call needs to be approved before it is allowed. This allows for multiple apps to run on the Clace server without interfering with each other.\nTo approve an app permissions, run\nclace app approve /disk_usage The --approve option can be specified during the app create and app reload command to automatically approve the permissions.","application-types#Application Types":"Clace allows easy management of multiple apps on one Clace server installation. There are three main types of Clace apps:\nAction apps - App backend is defined in Starlark and an auto generated form UI and report is created by Clace. These are the simplest apps. Containerized Apps - App backend (in any language/framework) runs in a container. Clace acts as an application server doing reverse proxying for the app APIs. This allows Clace to install and manage apps built in frameworks like Streamlit/Gradio/FastHTML/FastAPI/Flask etc. Hypermedia apps - The app is completely customizable, allowing combining containerized apps with actions and custom API handlers, building Hypermedia driven UIs. For all apps, Clace provides blue-green staged deployment, OAuth access controls, secrets management, TLS cert management etc.","containerized-applications#Containerized Applications":"Clace apps which are implemented in Starlark run within the Clace server. No containers are required for running those apps. For apps where the backend is implemented in any other language, containers are used to run the app. Clace works with Docker and Podman. By default, the server looks for the podman client CLI. If not found, it looks for the docker client CLI. To customize this, add in server config\nclace.toml[system] container_command = \"/path/to/container_manager_cli\" There are two options for using containerized apps. One is to include the required files in the app repo. This will mean there should be app.star with the app config, a Containerfile or Dockerfile with the container config. The other option is to use an app spec. This allows you to use Clace without requiring any changes to your app. No container file is even required. For example, the command\nclace app create --spec python-streamlit --branch master --approve \\ github.com/streamlit/streamlit-example /streamlit does the following:\nChecks out the github.com/streamlit/streamlit-example Copy any missing files from the app specification python-streamlit into the repo Load the app source and metadata into the Clace server metadata database (SQLite) When the first API call is done to the app (lazy-loading), the Clace server will build the container image from the Containerfile defined in the spec, start the container and setup the proxy for the app APIs.\nAny env params which need to be passed to the app can be configured as app params. Params are set, during app creation using app create --param port=9000 or after creation using param update port 9000 /myapp.\nIf the source repo has a Containerfile or Dockerfile, the container spec is a generic spec which works with any language or framework. If the container file defined a port using EXPOSE directive, then port is not required. Otherwise, specify a port, for example\nclace app create --spec container --approve --param port=8000 \\ github.com/myorg/myrepo /myapp See containerized apps for details.","developing-apps#Developing Apps":"Clace app backend can be written in any language, running in a container. Some apps can be written in Starlark and Go HTML templates, in which case no containers are required.\nSee dev overview for a quick start overview on developing Clace applications.","installation#Installation":"Install On OSX/Linux To install on OSX/Linux, run\ncurl -L https://clace.io/install.sh | sh source $HOME/clhome/bin/clace.env clace server start \u0026 Install On Windows To install on Windows, run\npwsh -Command \"iwr https://clace.io/install.ps1 -useb | iex\" Use powershell if pwsh is not available. Start a new command window (to get the updated env) and run clace server start to start the Clace service.\nInstall Apps Once Clace server is running, to install apps, run:\nclace app create --approve github.com/claceio/apps/system/list_files /files clace app create --approve github.com/claceio/apps/system/disk_usage /disk_usage clace app create --approve github.com/claceio/apps/utils/bookmarks /book The disk usage app is available at https://localhost:25223/disk_usage (port 25222 for HTTP). admin is the username, use the password printed by the install script. The bookmark manager is available at https://localhost:25223/book, the list files app is available at https://localhost:25223/files. Add the --auth none flag to the app create command to disable authentication.\nSee installation for details. See config options for configuration options. To enable Let’s Encrypt certificates, see Automatic SSL.\nThe release binaries are also available at releases. See install from source to build from source.","lifecycle-with-git#Lifecycle With Git":"If using git, a workflow would be:\nCreate a dev mode app, like clace app create --dev --approve ~/myappcode /myapp_dev Create a prod mode app, like clace app create --approve github.com/myorg/repo /myapp As code changes are saved to disk, the changes are immediately live at https://localhost:25223/myapp_dev When code is in a stable state, check in the dev code to git. Run clace app reload /myapp. This will update the staging app with the most recent code from main branch in git. The staging app is live at https://localhost:25223/myapp_cl_stage. Verify the functionality of the staging app. To promote the code to prod, run clace app promote /myapp. The staged code is promoted to prod, live at https://localhost:25223/myapp. ","lifecycle-without-git#Lifecycle without Git":"If not using git, a workflow would be:\nCreate a dev mode app, like clace app create --dev --approve ~/myappcode /myapp_dev Create a prod mode app, like clace app create --approve ~/myappcode /myapp As code changes are saved to disk, the changes are immediately live at https://localhost:25223/myapp_dev When code is in a stable state, run clace app reload /myapp. This will update the staging app with the most recent code from ~/myappcode folder. The staging app is available at https://localhost:25223/myapp_cl_stage for verification. To promote the code to prod, run clace app promote /myapp. The staged code is promoted to prod, live at https://localhost:25223/myapp. Having a staging environment helps catch issues related to account setup (which endpoint is pointed to etc) and other config issues before the changes are live on prod. Clace implements versioning for prod apps, even when source is not from git.","managing-applications#Managing Applications":"Multiple applications can be installed on a Clace server. Each app has a unique path and can be managed separately. The app path is made up of domain_name:url_path. If no domain_name is specified during app creation, the app is created in the default domain. The default domain is looked up when no specific domain match is found. See app routing for details about routing.\nFor local env, url based routing can be used or *.localhost domain can be used for domain based paths. For production deployment, if wildcard DNS is setup, domain based routing can be used without new DNS entries being required per app. Apps can be hosted on multiple unrelated domains on one Clace server.","staged-deployments#Staged Deployments":"For dev mode apps, there is just one app. For a prod mode app, creating the app creates a staging app and the actual production app. All config and code changes are applied on the staging mode app first, and then manually promoted using app promote. Promotion is automatic if --promote option is specified for the app reload (or any other command performing a metadata change).\nThe app list command lists all the apps for the specified glob pattern. By default, it lists only the dev and prod apps. To list the staging apps also, add the --internal (or -i) option to app list. all is a shortcut for *:**, which means all apps in all domains. all is the default for app list. For example:\nclace app list --internal all lists all the apps and internal apps for each app. clace app list \"example.com:**\" lists the main apps for the example.com domain.\nThe staging app can be used to verify whether changes are working before the production app is updated. The staging app is accessible by suffixing _cl_stage at the end of the prod app path. So for an app at https://example.com/, the staging url is https://example.com/_cl_stage. For an app at /utils/app1, the staging app url is /utils/app1_cl_stage.\nTo promote changes from staging to prod, run:\nclace app promote all or clace app promote \"/disk_usage*\" to promote specific apps. Use the --dry-run option to verify commands before they are actually applied."},"title":"Quick Start"},"/features/":{"data":{"":" GitOps Workflow Blue-green (staged) deployments, versioning and preview environments with no infra to manage.\nHypermedia web apps Fast and lightweight backend driven apps, minimal frontend complexity.\nSecrets Management Manage secrets with AWS Secrets Manager and Vault.\nAuto-Pause Idle apps Idle pause are paused, scale down to zero.\nAutogen Actions Auto generated UI for backend actions, no UI to develop.\nAudit Events Auto-audit logging for all events, plus custom events.\nCross-language AppServer Application Server which supports all languages.\nContainer management Automatically build and and deploy containers, with Docker or Podman.\nCross-platform support Clace runs on Linux, Windows and OSX, works with Docker and Podman\nAuto TLS Certificates Automatically generate TLS certificates, for multiple domains\nOAuth authentication Add OAuth2 or OIDC based authentication to any app\nDomain based and path based routing Install apps at a domain, subdomain or at path level\nPreview Apps Create preview apps from CI, allowing for changes to be reviewed before merge\nBlue-green Deployment Staged deployment, for code changes and for config changes\nSecurity Sandbox Apps using Starlark based micro-framework use sandboxing for security"},"title":"Features"}} \ No newline at end of file +{"/about/":{"data":{"":"","#":"What is Clace? Clace is an Apache-2.0 licensed project building a web app development and deployment platform for internal tools. Clace allows easy and secure hosting of multiple web apps, in any language/framework, on a single machine. Clace is cross-platform (Linux/Windows/OSX) and provides a GitOps workflow for managing web apps.\nProject Goals The goal of this project is to make it easy for individuals and teams to develop and manage lightweight full stack applications in a secure manner. Clace aims to make it easy to install and manage secure self-hosted web applications with minimal operational overhead. Easy integrations to enable SSO/SAML based authentication and authorization controls, audit logs and integration with secrets manager for managing credentials are goals.\nA single developer should be able to manage the full application lifecycle: frontend and backend development and production deployment. Deployments should support a GitOps approach, with automatic preview environment to verify changes before making them live. It should be easy, for the original developer or a new one, to make application code changes and deploy - after six months or after six years.\nWhat’s with the name The name Clace is a play on Command Line Ace, since building an UI for command line applications was an initial target use-case. The name is pronounced like Place, with a C.\nHow is Clace implemented? Single binary web application server (in golang), with a set of plugins built in (also in golang) which allow access to external endpoints. The server is statically configured using a TOML file. Applications are configured using Starlark, which is a subset of Python. Python is an ideal glue language, Starlark is used to configure the application backend logic Multiple applications can be dynamically installed, an embedded SQLite database is used to store application metadata (Postgres support is in the roadmap). For applications using the container plugin, Clace works with Docker or Podman using CLI to build and run the containers. Path based routing, each app identified by a unique path. Also, domain based routing, which allows multiple domains to point to the same Clace instance, with path based routing being done independently for each domain. Automatic TLS certificate management for each domain to simplify deployments. A sandboxing layer is implemented at the Starlark(python) to Golang boundary, allowing the implementation of security and access control policies. Go code is trusted, Starlark code is untrusted. For Starlark based apps, the application UI is implemented using Go HTML templates, with HTMX for interactivity. Go templates support context aware templating which prevents encoding related security issues. They also work well with the HTML fragments required for HTMX. No need to install any additional components like Python or NodeJS/NPM on the host machine. Integration with tailwindcss-cli is supported. esbuild (using the esbuild go library) is supported out of the box for importing ESM modules. Current Status The current status as of Aug 2024 is:\nClient and server (in a single binary) for service management and configuration. Support for application development with Starlark based configuration. Container management support with Docker and Podman Auto-idling of containers to reduce resource usage Go HTML template loading and caching for request processing. HTTP plugin for communicating with REST endpoints. Exec plugin for running system commands. Built in admin account for local development. Auto-sync (file system watcher) and Auto-reload using SSE (automatic UI refresh) for speeding up the application development cycle. Admin functionality using unix domain sockets for security. Application sandboxing checks to ensure only audited operations are allowed. Staged deployment support, preview app creations support. App data persistence to sqlite with managed tables. Who is behind this project? The project was started by Ajay Kidave. Ajay’s background has been in database systems and enterprise integration tools. Clace was started to find ways to reduce the development and operational complexity in tooling for internal applications.\nHow to stay in touch? Star the repo at github.com/claceio/clace Email at contact@clace.io Follow on Twitter Subscribe to the blog RSS feed Connect on Discord "},"title":"About"},"/blog/appserver/":{"data":{"":" 📎 Clace is an open source platform for developing internal tools and deploying them across a team. Clace can auto-generate an UI for backend actions. Clace is the first internal tools platform built for Hypermedia Driven Applications and first internal tools platform on the CNCF landscape. Application Servers can make application deployment easy. AppServers do not support all the features of a PaaS solution but that comes with the benefit of zero config deployments. Especially for internal tools, AppServers are a great alternative to building a deployment solution on top of Kubernetes. Clace is the first AppServer built for use with containers.","appserver-features-of-clace#AppServer Features of Clace":"Clace is built as a platform for teams to deploy internal tools. As part of that, Clace implements an AppServer to deploy containerized apps. The goal is to make it easy for teams to deploy and manage Streamlit/Gradio type apps for internal users. Clace provides blue-green staged deployment, GitOps, OAuth access control, secrets management etc for the apps.\nWith Clace, any Containerized app (having a Dockerfile) can be installed using a command like\nclace app create --spec container --approve github.com/\u003cUSERID\u003e/\u003cREPO\u003e /myapp The app will be available at the /myapp url. For many frameworks, zero config is required. Not even a Dockerfile is required. For example\nclace app create --spec python-streamlit --branch master --approve github.com/streamlit/streamlit-example /streamlit_app deploys a Streamlit based app.\nEach app has a dedicated url, domain based or path based. Clace ensures that no other app can conflict with that path. Clace can currently scale between zero and one instance of the container. More than one is not supported since Clace runs on a single machine (multi-node support is planned). Clace has a CLI interface currently, a declarative interface based on the CLI is planned.\nFor use cases where teams are deploying internal tools, Clace can provide a much simpler solution as against using a general purpose PaaS solution.\nUpdate (Jan 2025): Clace is now listed in the CNCF Landscape. ","appservers-miss-the-cloud-native-train#AppServers Miss the Cloud-Native Train":"Since the initial release of Docker in 2013, containers have become very popular for application deployment. Containers have the advantage of encapsulating the application runtime and dependencies in an easily deployable image. Being able to set resource limits on CPU/memory/disk usage enables isolation across applications.\nNo application server currently supports running apps within containers. App servers are missing from the crowded cloud native landscape (possibly the only infrastructure software component missing there).","cloud-native-appserver-features#Cloud-Native AppServer Features":"A cloud-native application server would include the following features:\nContainer-Based: Uses containers for application deployment, with isolation across apps. Easy Config: Provide zero config or simple config approach GitOps : App deployment driven by source code changes. Elastic Scalability: Scale down to zero, scale up as required based on load. Declarative Configuration: All configuration is applied declaratively as opposed to being imperative. The AppServer is not replacing the language specific services. For example, with Python, Gunicorn/Uvicorn would provide the WSGI/ASGI functionality within the container.","multi-language-appservers#Multi-Language AppServers":"Some of the prominent multi-language app servers are:\nNGINX Unit: Nginx Unit is a different application from the regular Nginx web server. Unit is configured through JSON based APIs. Packaging apps to work with Unit is not straightforward. For example, see Unit Java sample. uWSGI: uWSGI is a “full stack for building hosting services”. It supports many powerful features for process management. Combining all its features and configuring them correctly is non-trivial. Many languages are supported but outside of interpreted languages, it is not easy to configure. For example, see uWSGI Java config. The uWSGI project is currently in maintenance mode. Phusion Passenger: Phusion Passenger primarily supports Ruby, Python and Javascript. Passenger 6 added support for generic apps. This requires changing the app to pass the port to use on its command line. For example, Passenger Java. These projects either run the app in-process or use a process model to run each application separately. The in-process model has issues with ensuring stability of apps when another app misbehaves. Even with the multi-process model, complete isolation across apps is not supported.","paas-vs-appservers#PaaS vs AppServers":"Most of the recent innovation in the container orchestration space have focussed on providing support for hosting the complete software stack. This includes deploying stateless applications, stateful databases, object stores and any other type of application. The goal has been to build Platform-As-A-Service solutions (PaaS). Kubernetes is built as a platform for building platforms. Even beyond Kubernetes, most container deployment platforms focus on trying to provide a complete PaaS solution. Since the scope of applicable use cases is large, even the simplest use case requires complex configuration with a PaaS solution.\nAppServers by definition are simpler. They support deploying stateless applications. Given the source code for a service, an AppServer can run the service and give an HTTP endpoint to access the service. AppServer can provide a standardized deployment approach, irrespective of the language/framework. AppServers do not support deploying databases or queues or object stores.\nMany teams choose managed services (like AWS RDS or MSK) for data persistence. If the stateful applications are externally managed, then AppServers can be used for deploying the stateless applications. This avoids the complexity of having to maintain PaaS configurations.","what-is-an-application-server#What is an Application Server":"An Application Server is a service that makes it easy to deploy applications and provides common features required by most apps. This includes implementing connection handling (HTTP request routing), deployment and versioning, logging, monitoring and authentication features.\nMany application servers are programming language specific. In the Java and .Net ecosystems, class loader level isolation is used to implement app servers. The request processing model of PHP also makes app servers suitable for serving PHP apps. For interpreted languages like Python and Ruby, there are some app servers which provide cross language support. App server support is limited for compiled languages. This article will focus on app servers which support multiple languages, since those are more widely usable."},"title":"Missed Connections: AppServers in the Containerized Landscape"},"/blog/errors/":{"data":{"":" 📎 Clace is an open source platform for developing internal tools and deploying them across a team. Clace can auto-generate an UI for backend actions. Clace is the first internal tools platform built for Hypermedia Driven Applications and first internal tools platform on the CNCF landscape. ","background#Background":"Most programming languages handle error conditions either by supporting error values or by means of exceptions. Error return value support varies, with some statically typed languages enforcing that the error conditions be handled. With exceptions, the invoker can either catch and handle it or it automatically gets thrown up the stack. The purpose of error handling is to allow an invoker to either handle the issue or allow the invoker to return the error to the caller.\nExceptions have the advantage of the error handling being automatic. Error values have the advantage of requiring more explicit error handling, at the cost of verbosity.","can-this-be-a-generic-solution#Can this be a generic solution?":"The Clace runtime provides all the APIs used by Clace apps by means of plugin calls. This solution can be applied when\nAll code that can cause errors are provided through a standard API interface Thread locals are feasible for tracking errors There is a standard error handling function which does something useful (could be user defined) The error check happens at the API boundary (Starlark to Go in this case). If there is code which does excessive CPU usage or memory allocation, that code will run before the automatic error check kicks in. That should not be an issue in practice for glue code as used by Clace.\nThis error handling solution is limited in scope to use cases where glue scripts are being written which make lots of API calls. This provides a shell errexit type feature for regular code. This does not support error handling that needs to happen within user defined code, like one function which returns an error to be handled by another function.\nHandling resource leaks is another concern. For Clace, since all resources (transactions, result sets etc) are created through the plugin API, they are automatically closed when an error occurs.","error-handling-for-glue-code#Error handling for Glue code":"For code which is gluing together multiple API’s, error handling can be tedious. Some languages have specific support for this. The most famous example is the errexit setting set -e in shell scripts. This will automatically check each command for error return status and fail the script if an error occurs.","how-does-this-work#How does this work?":"The automatic error handling feature of Clace keeps track of every plugin call’s status. If the plugin call fails, the Clace runtime makes sure that return value cannot be accessed, unless an explicit error check was done. If no explicit check is done, the Clace runtime will fail the API, calling the user defined error handler or a generic error handler if none is defined. So for the code\ndef insert(req): store.begin() book = doc.bookmark(\"abc\", []) ret = store.insert(table.bookmark, book) print(ret.value) store.commit() If begin() fails, the call to insert() will fail since the previous error was not handled (begin’s error message is raised). If insert() fails, the value access will fail, so the print will not run If commit() fails, the Clace runtime will first check whether the last plugin failed before handling the API response. Thread locals are used to track errors across plugin API calls. This works since an API handler Starlark function is single threaded. When begin() fails, it sets a thread local. If the error is explicitly checked, like\nret = store.begin() if ret.error: pass print(ret.value) then the thread local state is cleared. So if the code is doing explicit error checks, the automatic error handling is disabled.","is-there-a-third-option#Is there a third option?":"Clace is built to be a platform for building internal tools. Clace is built in Go and uses Starlark for app configuration and also for API business logic. Starlark does not support exceptions and does not support multi value returns. This makes error handling difficult. The solution implemented for Clace is an API boundary error checker with the following properties:\nAutomatic error handling, no explicit error checks required for every API call Easy way to do explicit error checks when errors are expected This gives the best of both worlds. All error conditions are automatically checked like exceptions. When explicit error checks are required, they are easy to do like error values.","trade-offs#Trade-Offs":"The two main trade-offs we are handling are:\nExceptions are automatically checked, but they result in error handling being done away from the code which actually failed. Error values need to be checked explicitly, but the error handling code is local to where the error occurred. In most languages, it is easy to miss checking for errors (Rust being one exception with the Result type) The ideal scenario in terms of code verbosity is that error handling should be automatic. The ideal scenario in terms of proper error handling is that the explicit error checks should be easy for the invoker, else the automatic error handling kicks in."},"title":"Errors and Exceptions: Is there a third option?"},"/blog/go-composition/":{"data":{"":" 📎 Clace is an open source platform for developing internal tools and deploying them across a team. Clace can auto-generate an UI for backend actions. Clace is the first internal tools platform built for Hypermedia Driven Applications and first internal tools platform on the CNCF landscape. ","background#Background":"I recently encountered an issue where Server-Sent Events (SSE) stopped working in Clace. SSE are used for live reload functionality in Clace. The problem turned out to be a recent change in Clace which added support for tracking HTTP response status code. This was implemented by implementing a composition over the http.ResponseWriter to keep track of the status code. This composition broke the SSE functionality.","composition-breaks-implicit-interfaces#Composition breaks Implicit Interfaces":"The reason for the issue encountered is an implicit interface http.Flusher implemented by most implementations of http.ResponseWriter. Adding a composition over http.ResponseWriter causes this implicit interface to no longer be implemented.\ntype CustomWriter struct { http.ResponseWriter statusCode int } Here, CustomWriter no longer implements http.Flusher, even if the underlying http.ResponseWriter implementation did. SSE was supported for flushable writers only, so adding the composition broke SSE. The fix is to have the composing struct explicitly implement http.Flusher. See go playground to see an example.","composition-over-inheritance#Composition over Inheritance":"Go supports embedding, which can be used to implement Composition (has-a relationship) rather than inheritance (is-a relationship). Composition has some benefits over inheritance. Embedding in Go allows the use of composition without requiring forwarding methods.","fixing-the-issue#Fixing the issue":" func (cw *CustomWriter) Flush() { if flusher, ok := cw.ResponseWriter.(http.Flusher); ok { flusher.Flush() } } Adding a Flush function is a fix, but an issue with this is that if the caller had support for non-flushable writers, that behavior is lost. A better fix is to have two implementation, one flushable and another non flushable. The appropriate one should be used based on whether the underlying writer implements flusher. This way, the original behavior is not changed by adding the composition.\ntype FlushableWriter interface { http.ResponseWriter http.Flusher } type FlushableCustomWriter struct { FlushableWriter statusCode int } type CustomWriter struct { http.ResponseWriter statusCode int } Some HTTP routers like Chi have middleware which implement the required implicit interfaces.","how-to-avoid-this-issue#How to avoid this issue":"The Go type system does not have a way to catch such issues are compile time. At runtime, the issue can show up as a performance degradation (if the implicit interface is used as an performance optimization) or as a unexpected behavior (if custom behavior is implemented using the implicit interface).\nIt would have helped if the documentation for http.ResponseWriter had mentioned the http.Flusher interface and when it is used. This is feasible when the types are in the same package.\nThe takeaway is that if using composition over types which could have implicit interfaces, it is important to look at whether any of those implicit interfaces have to be explicitly implemented by the composing type.\n💬 Discussion thread on Reddit ","implicit-interfaces#Implicit Interfaces":"Interfaces in Go are implemented implicitly. This is a powerful feature, allowing interfaces to be added when required later. Interfaces can even be created for types in different packages. This allows clients to control how types are used rather than depending on how the types were originally defined.","stdlib-implicit-interfaces#Stdlib Implicit Interfaces":"The Go stdlib uses implicit interfaces to implement optimizations (like io.WriterTo and io.ReaderFrom) and custom behaviors (like fmt.Stringer). Other similar interfaces are http.Hijacker, http.Pusher, and io.Closer."},"title":"Go Composition does not compose well with Implicit Interfaces"},"/blog/intro/":{"data":{"clace-platform-for-managing-internal-tools#Clace: Platform for Managing Internal Tools":" 📎 Clace is an open source platform for developing internal tools and deploying them across a team. Clace can auto-generate an UI for backend actions. Clace is the first internal tools platform built for Hypermedia Driven Applications and first internal tools platform on the CNCF landscape. Clace: Platform for Managing Internal ToolsClace is an open-source platform to enable easy development and deployment of web applications for internal tools. The goals for the Clace project are:\nEnable development and deployment of secure internal web applications. Simplify ongoing maintenance of such apps by removing build and dependency related issues. Updating an app after six months or six years should just work. Provide portable and flexible deployment options, easy to use on developer machines and also on a shared server across teams. ","current-status#Current Status":"Clace is in a beta state currently. Custom application support is functional. Support for loading plugins dynamically is in progress. You can try out Clace (on OSX, Linux or Windows with WSL) by doing:\ncurl -L https://clace.io/install.sh | sh source $HOME/clhome/bin/clace.env clace server start \u0026\u0026 sleep 2 clace app create --approve github.com/claceio/apps/system/disk_usage /disk_usage The app should be available at https://127.0.0.1:25223/disk_usage after allowing the self-signed certificate. admin is the username, use the password printed by the install script. See installation for details.","follow-along#Follow Along":"You can keep in touch by these means:\nStar the repo at github.com/claceio/clace Sign up for Email updates Follow on Twitter Subscribe to the blog RSS feed Follow ClaceIO on LinkedIn Connect on Discord Use discussions feature in Github or raise issues to provide feedback.","how-does-it-work#How does it work?":"Clace applications are configured in Starlark, which uses a subset of Python syntax. The API routes are defined to be Hypermedia first, using HTML templates to drive the UI interactions. Templates are written using Go HTML templates. HTMX is used for server interactions. The backend code runs in a security sandbox and every access to plugins need to be explicitly permitted. Application updates can be done with no build step required. Clace integrates with TailwindCSS/DaisyUI for styling and has esbuild built-in for ESM support.","security#Security":"The Starlark backend code for Clace runs in a sandbox, all external interactions need to go through plugins. The Clace platform implements a security sandbox at the plugin boundary. Applications define what operations they need to be able to perform. The platform enforces these permissions at runtime.\nThis security model enables the following:\nUsers can download applications and run on their machine, without worrying about what operations the app can do on their system outside the audited permissions. Operations teams can install and approve applications. Further application updates can be handled by the development team, without requiring the operational admins to verify the updated code. As long as the application works within the originally defined permission boundary, application updates will continue to work. Application developers can use LLM powered automated code generation tools without worrying about the side-effects of the code. If the generated code tries to perform any operation not previously approved, it will fail. The sandbox will ensure that the apps can do only authorized operations. This makes Clace an ideal target for LLM (like GPT) generated applications. The Clace platform will add the authentication/authorization, gitops based deployment and operational monitoring features on top of the generated app.","use-cases#Use-cases":"Clace is built to solve two different types of use-cases:\nCustom applications: With fully customizable UI, this would be similar to solutions like Retool. A low-code approach is used, with a focus on Hypermedia driven applications. Actions: This would be similar to solutions like Rundeck. A way to automate internal applications, with a form based interface, with support for triggered and scheduled execution. One of the aims of Clace is to make it possible for everyone, especially backend engineers, to develop and use simple web interfaces. For use-cases where a CLI was developed previously, a Clace based UI could be built. The backend service could invoke the CLI command or directly call the internal API which need to be exposed. Development and use of simple web interfaces for all types of use-cases should be made easier with Clace."},"title":"Introducing Clace"},"/blog/sqlite/":{"data":{"":" 📎 Clace is an open source platform for developing internal tools and deploying them across a team. Clace can auto-generate an UI for backend actions. Clace is the first internal tools platform built for Hypermedia Driven Applications and first internal tools platform on the CNCF landscape. ","background#Background":"Clace is built to serve web applications, primarily for internal tools. Clace provides functionality usually handled separately by a web server and an application server. When the development of Clace was started last year, one of the first decisions was how to store the application data (files) and metadata. The app metadata obviously made sense to store in a database, since apps are created dynamically. The app data (static files, app code, config files etc) is usually stored on the file system by most web servers.","benefits-of-using-sqlite#Benefits of using SQLite":"The decision to use SQLite for file storage has provided lots of additional benefits (some unanticipated).\nTransactional Updates : This is the main benefit. Updating multiple files can be done in one transaction. Isolation ensures that there are no broken webapps during the update.\nDeployment Rollbacks: Another of the transactional benefits is the ability to roll back deployment in case of errors. If multiple apps are being updated, all of them can be rolled back in one go. Rolling back a database transaction is much easier than cleaning up files on the file system.\nFile De-duplication Across Versions: Clace automatically versions all updates. This can lead to lots of duplicate files. The file data is stored in a table with the schema\nCREATE TABLE files (sha text, compression_type text, content blob, create_time datetime, PRIMARY KEY(sha)); The uncompressed content SHA256 hash is used as the primary key to store the file data. This means that even if multiple versions of an app have the same file, the file contents are stored only once.\nDe-duplication Across Apps : Each production app in Clace has an staging app. Apps can have multiple previews apps. This can lead to lots of duplication of files. Using the database helps avoid all the duplication. Even across apps, there are files which have the same contents. Files are de-duplicated across apps also.\nEasy Backups: Using SQLite means that backups are easy. The state of the whole system, metadata and files can be backed up easily using SQLite backup tools like Litestream.\nContent Hashing: For content caching on the browser, web servers return a ETag header. Using the database for files makes it easy to save the content SHA once during file upload without having to recompute it later.\nCompression: The file contents are saved Brotli compressed in the SQLite table. The database approach has the advantage that contents can be saved in multiple formats easily. GZip compressed data and uncompressed data can be added by just adding a column in the files table.","multi-node-support#Multi-Node Support":"Clace currently runs on a single node. When multi-node support is added later, the plan is to use a shared Postgres database instead of using local SQLite for metadata and file data storage. This will come with latency issues. The plan is to use a local SQLite database as a file cache to avoid latency while accessing Postgres.","performance#Performance":"For Clace, the SQLite database approach provides great performance. There is no equivalent implementation using the file system to compare against, so a direct benchmark test is not done. Based on benchmarking done by the SQLite team, SQLite can have better performance than direct file system use for some workloads.","using-sqlite-for-serving-files#Using SQLite for serving files":"For Clace, the decision was made to use SQLite for app files storage instead of using the file system. The reasoning was mainly to be able to do atomic version changes. When updating an app, since there could be lots of files being updated, using a database would allow all changes to be done atomically in a transaction. This would prevent broken web pages from being served during a version change.\nClace uploads all files to the SQLite database during app creation and updates. Files are uploaded from GitHub or from local disk. Only for development mode, the local file system is used.","why-this-approach-is-not-more-common#Why this approach is not more common?":"One of the reasons most web servers use the file system is convenience. File updates can be done using any file system tool: rsync, tar etc work for copying files over. The other reason is probably historical: file systems are what were used before there were good in-process relational databases available. Using a database means some kind of API interface is required for uploading files, which is not always feasible.\n💬 Discussion thread on Hacker News "},"title":"Using SQLite as Storage for Web Server Static Content"},"/blog/webserver/":{"data":{"":" 📎 Clace is an open source platform for developing internal tools and deploying them across a team. Clace can auto-generate an UI for backend actions. Clace is the first internal tools platform built for Hypermedia Driven Applications and first internal tools platform on the CNCF landscape. ","app-level-isolation#App-Level Isolation":"The goal of app-level isolation is for developers to confidently update web server routing rules, being sure that a broken update will not impact other apps.\nTo enforce isolation across apps, web servers first need a concept of an app. There needs to be a way to say which rules are for which app. Each update has to be scoped to be for a particular app. If there are rules which conflict with another app, those rules need to be rejected.","background#Background":"Clace is a platform for developing and deploying internal tools. Clace provides functionality usually handled separately by a web server and an application server. The web server part of Clace is built such that there is complete isolation between app-level routing rules. Creating a new app or updating an existing app cannot break other apps. This post goes into details about how this is done and why it is useful.","clace-approach#Clace Approach":"Clace has the concept of an app where each app is installed for a path within a domain. If app A is installed at appA.example.com:/, then that domain is completely dedicated for that app. An app installed at appB.example.com:/test owns the /test/* namespace for domain appB.example.com, no other app can use that namespace.\nThis approach has the benefit that each app gets a dedicated namespace. Within that namespace, the app can do whatever it wants, without interfering with other apps. At app installation time, a check is done whether the domain path being requested is available. If some other app is using that path, the new app installation is rejected. A SQLite database is used to store app metadata, so API driven app updates do not conflict with each other.\nWithin the app, rules are defined using Starlark. This avoids the need to learn a new DSL. The routing config is read at app initialization only and the actual handler logic can be expressed alongside the routing rules. For containerized apps, the routing rule specifies the revery proxy url. Reloading an app updates its config, without impacting other apps.","conclusion#Conclusion":"By enforcing app-level isolation in routing rules, Clace allows each app to manage its own domain and path namespace without risking conflicts or breakages. This approach encourages developers to utilize efficient web server–level routing features, confident that changes in one app won’t disrupt others. Some webserver routing use cases which are more complex cannot use this approach, but this is useful for app deployment scenarios.","updating-routing-rules#Updating Routing Rules":"Most web servers use a config file for specifying the rules for request routing. The config file is generally a DSL which specifies the API rules. Some servers like Caddy support JSON input. Encapsulating or grouping together rules related to an app is supported, but this encapsulation is not enforced. For updating the rules, the approach is to update the file on disk and send a reload request to the web server. Even for API based updates, the approach is usually to send the whole config contents, partial updates are not supported.\nThe issue with this approach is that if there are multiple apps, updating the rules for one app can break other apps. There is no enforcement of isolation across apps. This results in app developers trying to avoid updating the web server, doing more in the application server when it would have been more efficiently done in the web server.","web-server-overview#Web Server Overview":"A Web Server is software that accepts HTTP/HTTPS requests and routes them appropriately. Web Servers provide features like URL rewrites, reverse proxying, header manipulation, static file serving, and WebSocket connection handling. Web Servers can accept connections on multiple ports. A common pattern is that all requests are received on port 80 (for HTTP) and 443 (for HTTPS) and routing is done based on request domain (from the Host HTTP header) and path (from the url). Apache, Nginx, and Caddy are popular web servers."},"title":"App-Level Isolation in Web Server Config"},"/docs/":{"data":{"":"Clace is an open-source Apache-2.0 licensed project for easy development and deployment of self-hosted web apps. Clace provides a web application server focussed on securely running multiple applications on a single installation.\nQuick StartQuick overview of using Clace InstallationDetails about installing Clace server ConfigurationClace server configuration details Managing appsWorking with Clace apps Developing appsDeveloping Clace apps PluginsDetails about Clace plugins "},"title":"Docs"},"/docs/actions/":{"data":{"":"Actions allow apps to expose an autogenerated UI for simple backend actions. For use cases where an existing CLI application or API needs to be exposed as a web app, actions provide an easy solution. An app can have one or more actions defined. Each action has to be given a unique path, which does not conflict with any other route defined for the app. See weather app code:demo for an example of using Actions.","action-definition#Action Definition":"An action is defined using the ace.action struct. The fields in this structure are:\nProperty Optional Type Default Notes name false string The action name path false string The path to use within app path run false function The function to run on execution suggest true function none The function to run on suggest description true string none The description for the action hidden true list strings none The params which should be hidden in the UI for this Action show_validate true boolean False Whether to show an Validate option for this action The name and description are shown in the app UI. The app params are displayed in a form. BOOLEAN types are checkboxes, others are text boxes.\nWhen the form is submitted, the run function is called. The params are passed as an args argument. The response as returned by the handler is shown on the UI.\n⚠️ In the action handler function, use args argument to get the values from the form. Referencing params will give the default values for the parameters, not the actual values passed in. The hidden property can be used to hide params for specific Actions. Set it to the list of params to hide, for example hidden=[\"param1\"].","action-result#Action Result":"The handler returns an ace.result struct. The fields in this structure are:\nProperty Optional Type Default Notes status true string The action status message values true list [] The actions output, list of strings or list of dicts report true string ace.AUTO The type of report to generate. Default is ace.AUTO, where it is selected based on response type. Other options are ace.JSON, ace.TEXT, ace.TABLE, ace.DOWNLOAD and ace.IMAGE. Any other value is a custom template name. param_errors true dict {} The validation errors to report for each param. The key is the param name, the value is the error message ","custom-templates#Custom Templates":"If the report type is set to any value other than ace.AUTO/TEXT/JSON/TABLE, that is treated as a custom template to use. The template should be defined in a *.go.html file. Either the file name can be used or a template/block name can be used. See template for details.\nFor styling, Clace uses DaisyUI by default, so default styles are reset. The custom template can use inline styles or it can use TailwindCSS/DaisyUI. For DaisyUI, the app has to be run in dev mode first for the style.css to be generated. See styling for details.\nSee dictionary code:demo for an actions example app which shows different type of reports.","display-types#Display Types":"For string type params, the display_type property can be set to FILE, PASSWORD or TEXTAREA. If no value is set, the field shows as a text input box. FILE param shows as a file upload input. PASSWORD shows as a password input. TEXTAREA shows as a text area.","file-handling#File Handling":"For FILE display type, the Action app user can upload a file. The file is uploaded to a temp file on the server and the file name is available through the args.param_name. The file can be process as required from disk. Multiple FILE type params are supported, each param can upload one file only. The temp files are deleted at the end of the handler function execution.\nTo return file as output for the Action, using the fs.serve_tmp_file API. This makes a file on disk available through an API.\nSee number_lines app code:demo for an example of using this API. Use report=ace.DOWNLOAD property in the ace.result to generate a file download link.\nFiles from the system temp directory and from /tmp are accessible by default for serve_tmp_file API. The file is deleted from disk by default after the first download. This can be configured at the system level using\nclace.toml[app_config] fs.file_access = [\"$TEMPDIR\", \"/tmp\"] To set this at the app level, run\nclace app update-metadata conf --promote fs.file_access='[\"/var/tmp\", \"$TEMPDIR\", \"/tmp\"]' /myapp ","multiple-actions#Multiple Actions":"Multiple actions can be defined for an app. Each action should have a dedicated path. If there are multiple actions, a switcher dropdown is automatically added for the app. The order of entries in the dropdown is the same order as defined in the app.\nSee weather app code:demo for an example of using multiple actions in one app.","param-value-selector#Param Value Selector":"For some params, it is useful to be able to provide a list of values from which the user can choose. The way this is supported is by using an options param. If param1 is a param which should show up as a selector, then define another param with the name options-param1, of type LIST. Set a default value for options-param1 with the values to show in the selector dropdown. For example\nparams.starparam(\"param1\", description=\"The param1 description\", default=\"option1\") param(\"options-param1\", type=LIST, description=\"Options for param1\", default=[\"option1\", \"option2\"]) In the UI, options-param1 is not displayed. param1 is show with a selector, having option1 and option2 as options. See dictionary for an app which uses this.\nThis approach is used for flexibility, instead of directly allowing the options to be configured for the param. The options param approach has the flexibility that when an app is installed, the options can be configured for the installation. This avoids having to maintain different copies of the app code. For example:\nclace app create --approve --param options-param1='[\"option1\", \"option2\", \"options3\"]' /mycode /myapp adds a new options3 option.","report-types#Report Types":"The response values can be a list of string or a list of dicts. The report is generated automatically by default. For list of strings, the report is a TEXT report. For list of dicts, the report can be either\nTABLE - selected if all dict values for the first row are simple types JSON - selected if any of the values for the first row is a complex type (like dict or list) For TABLE report, the fields from the first row are used as columns. Extra fields in subsequent rows are ignored. For JSON report, a JSON tree representation of each row is shown. The report type can be set to specific type instead of using AUTO.","sample-action#Sample Action":"First, define the parameters to be exposed in the form UI. Create a params.star file with the params. For example,\nparams.starparam(\"dir\", description=\"The directory to list files from\", default=\"/tmp\") param(\"detail\", type=BOOLEAN, description=\"Whether to show file details\", default=True) This app defines a run handler which runs ls on the specified directory. The output text is returned.\napp.starload (\"exec.in\", \"exec\") def run(dry_run, args): if args.dir == \".\" or args.dir.startswith(\"./\") or args.dir == \"..\" or args.dir.startswith(\"../\"): return ace.result(\"Validation failed\", param_errors={\"dir\": \"relative paths not supported\"}) cmd_args = [\"-Lla\" if args.detail else \"-La\", args.dir] out = exec.run(\"ls\", cmd_args) if out.error: return ace.result(out.error) return ace.result(\"File listing for \" + args.dir, out.value) app = ace.app(\"List Files\", actions=[ace.action(\"List Files\", \"/\", run, description=\"Show the ls -a output for specified directory\")], permissions=[ ace.permission(\"exec.in\", \"run\", [\"ls\"]), ], ) The app, when accessed will look as shown below, with the ls command output displayed:","suggest-handler#Suggest Handler":"If a suggest handler is defined for an action, then a Suggest button shows up in the UI. Suggest allows property values to be populated dynamically. For example, if the app has three params A, B and C, and all are empty initially. The first suggest can do return {\"A\": [\"avalue1\", \"avalue2\", \"avalue3\"]}. This will populate the A param with a dropdown. A subsequent suggest call can populate the value for B, with a list of options or with an actual value. The suggest handler is optional. A sample suggest handler is\napp.stardef suggest(args): if not args.A: alist = [] res = store.select(table.adata, {}) for aval in res.value: alist.append(aval.name) return {\"A\": alist} else: if not args.B: res = store.select_one(table.adata, {\"A\": args.A}) return {\"B\": res.value.bval} return {} See weather app code:demo for an example of using suggest.","validating-params#Validating Params":"The run handler can validate the parameters. If there are errors, it can return a validation error like\napp.star def run(dry_run, args): if args.dir == \".\" or args.dir.startswith(\"./\") or args.dir == \"..\" or args.dir.startswith(\"../\"): return ace.result(\"Validation failed\", param_errors={\"dir\": \"relative paths not supported\"}) if dry_run: return ace.result(\"Validation successful\") # Actual code for run handler Errors can be reported for multiple params. If the action definition has show_validate=True, then a Validate option will show up in the UI. Calling that will invoke the run handler with dry_run=True. The run handler should return after the param validation when dry_run is true."},"title":"Action Apps"},"/docs/app/":{"data":{"":"Details about developing Hypermedia driven Clace applications, managing API routes and HTML templates.\nOverviewOverview of a Clace app, sample apps Request RoutingDefining HTML, API and Proxy routes RequestDetails about the request structure passed to the handler ResponseDetails about how handler response is handled "},"title":"Hypermedia Apps"},"/docs/app/overview/":{"data":{"":"Clace supports deploying any type of app, in any language/framework, using containerized apps. Apps can also be built where the backend API is in the container but the UI is built using Clace. Clace UI applications implement a Hypermedia driven approach for developing web applications. Applications return HTML fragments as API response using Go html templates. The UI uses HTML enhanced with hypermedia controls using the HTMX library to implement user interactions.\nThe backend API routes and dependencies like CSS library, JavaScript modules etc are configured using Starlark configuration. Any custom API handling required is implemented in handler functions also written in Starlark. Starlark is a subset of python, optimized for application configuration use-cases.","app-types#App Types":"Clace apps can be of few different types:\nAction apps: Actions allow apps to expose an autogenerated UI for simple backend actions. Containerized apps: The whole app is implemented in a container, Clace proxies the container results. This uses the container and proxy plugins. No Starlark code is required for this. Starlark apps: These use the Clace plugins to implement the whole app. No containers are required for such apps. Hybrid apps: The backend APIs are implemented in a container. Clace is used to implement the Hypermedia based UI using Starlark handlers. This uses the http plugin to talk to the backend API and the container plugin to configure the backend. This section of the docs covers Starlark and Hybrid apps. For a containerized app, the --spec definition includes all the app definition. There is no need to do any custom Starlark config for a regular containerized app.\nSample Starlark App To create an app with a custom HTML page which shows a listing of files in your root directory, create an ~/myapp4/app.star file with\napp.starload(\"exec.in\", \"exec\") def handler(req): ret = exec.run(\"ls\", [\"-l\", \"/\"]) if ret.error: return {\"Error\": ret.error, \"Lines\": []} return {\"Error\": \"\", \"Lines\": ret.value} app = ace.app(\"hello4\", custom_layout=True, routes = [ace.html(\"/\")], permissions = [ace.permission(\"exec.in\", \"run\", [\"ls\"])] ) and an ~/myapp4/index.go.html file with\nindex.go.html\u003c!doctype html\u003e \u003chtml\u003e \u003chead\u003e \u003ctitle\u003eFile List\u003c/title\u003e {{ template \"clace_gen_import\" . }} \u003c/head\u003e \u003cbody\u003e {{ .Data.Error }} {{ range .Data.Lines }} {{.}} \u003cbr/\u003e {{end}} \u003c/body\u003e \u003c/html\u003e Run clace app create --auth=none --dev --approve ~/myapp4 /hello4. After that, the app is available at /hello4. Note that the --dev option is required for the clace_gen_import file to be generated which is required for live reload.\nThis app uses the exec plugin to run the ls command. The output of the command is shown when the app is accessed. To allow the app to run the plugin command, use the clace app approve command.\n⚠️ Note: If running on Windows, change ls to dir. Else, use the fs plugin to make this platform independent. See https://github.com/claceio/apps/blob/main/system/disk_usage/app.star. Custom Layout HTML App To return HTML response, a HTML template file named *.go.html is required. Create an ~/myapp2/app.star file containing\napp.starapp = ace.app(\"hello2\", custom_layout=True, routes = [ace.html(\"/\")] ) and an ~/myapp2/index.go.html file containing\nindex.go.htmlhello world2 Run clace app create --auth=none ~/myapp2 /hello2. After that, the app is available at /hello2\n$ curl localhost:25222/hello2 hello world2 The ~/myapp2/index.go.html can be updated to have a complete HTML page. Use the command clace app reload --promote /hello2 to pick up changes. This app is using custom_layout=True which means the app developer has to provide the complete HTML.\nDefault Layout HTML App The default is custom_layout=False meaning app developer has to provide only the HTML body, Clace will automatically generate the rest of the HTML. For using the auto generated HTML templates, the app has to be created in dev mode using the --dev option.\nCreate an ~/myapp3/app.star file containing\napp.starapp = ace.app(\"hello3\", routes = [ace.html(\"/\")] ) and an ~/myapp3/app.go.html file containing\napp.go.html{{block \"clace_body\" .}} hello world3 {{end}} Run clace app create --auth=none --dev ~/myapp3 /hello3 . After that, the app is available at /hello3. Note that the --dev option is required for the index_gen.go.html file to be generated.\nThere is only one route defined, for page /, which shows a HTML page with the name of the app. The body is generated from the contents of the app.go.html file. A more verbose way to write the same app config would be\napp.starapp = ace.app(name=\"hello3\", custom_layout=False, routes = [ace.html(path=\"/\", full=\"index_gen.go.html\")] ) ","automatic-error-handling#Automatic Error Handling":"To enable automatic error handling (recommended), add an error_handler function like:\napp.stardef error_handler(req, ret): if req.IsPartial: return ace.response(ret, \"error\", retarget=\"#error_div\", reswap=\"innerHTML\") else: return ace.response(ret, \"error.go.html\") "},"title":"Overview"},"/docs/app/request/":{"data":{"":"","accessing-inputs#Accessing Inputs":"Url Parameters For a route defined like\napp.starace.html(\"/user/{user_id}/settings\", \"index.go.html\") the url parameter user_id can be accessed in the handler\napp.stardef handler(req) user_id = req.UrlParams[\"user_id\"] Wildcard parameters are allowed at the end of the route. These are defined as\napp.starace.html(\"/path/*\", \"index.go.html\") and can be accessed as\napp.stardef handler(req) user_id = req.UrlParams[\"*\"] Regexes are also allowed in the path, these are defined as ace.html(\"/articles/{aid:^[0-9]{5,6}}\") and accessed as req.UrlParams[\"{aid}\"]. The route will match only if the regex matches.\nQuery String Parameters Query string parameters can be accessed as\napp.stardef handler(req) name = req.Query.get(\"name\") name = name[0] if name else None The value for Query is an string array, since there can be multiple query parameters with the same name.\nForm Data Form data can be accessed like\napp.stardef handler(req) name = req.Form.get(\"name\") name = name[0] if name else None The value for Form is an string array, since there can be multiple form parameters with the same name.","request-structure#Request Structure":"The handler function is passed one argument which has the details about the API call. The fields in this structure are:\nProperty Type Notes AppName string The app name in the config AppPath string The path where the app is installed. If root, then empty string AppUrl string The url for the app root PagePath string The path for the current page. If root, then empty string PageUrl string The url for the current page Method string The HTTP method, GET/POST etc IsDev bool Is the app installed in dev mode IsPartial bool Is this an HTMX driven partial request RemoteIP string The Client IP address UrlParams dict The url parameters, if used in the url spec Form dict The form data, including body and query Query dict The url query data, as a string array PostForm dict The form data from the body Data dict The response from the handler function (passed to the template) "},"title":"Request"},"/docs/app/response/":{"data":{"":"","custom-response#Custom Response":"In some cases, a custom response need to be generated, with special headers. Or the response needs to use a template different from the one defined in the route, which could happen in the case of an error. For such cases, a ace.Response structure can be returned by the handler. The fields in this structure are:\nProperty Optional Type Default Notes data false object The response data block true string Optional only if type is “json” type true string inherited from the route type definition If “json”, block is ignored code true int 200 HTTP status code retarget true string HX-Retarget header value, CSS selector to target, like “#error_id” reswap true string HX-Reswap, like “outerHTML” For example, this handler code uses retarget to handle errors by updating the html property which has id “gameErrorId”\napp.starret = http.post(api_url).json() if ret.get(\"Error\"): return ace.response(ret, \"game_error_block\", retarget=\"#gameErrorId\") return fetch_game(req, game_id) This code returns a 404 with a custom body generated from a template block called “invalid_challenge_block”\napp.starif challenge.get(\"Error\"): return ace.response(challenge, \"invalid_challenge_block\", code=404) ","json-response#JSON Response":"All responses are HTML by default, as required for building a proper Hypermedia client. There are some cases where data needs to be returned to the client in JSON format. The type property can be used for those cases. For example, this API returns JSON\napp.starace.api(\"/memory\", handler=memory_handler), Here, the response from the handler function is returned as JSON, no template is used. Also, in this handler, if there is a call to ace.Response, the type will default to JSON since that is the type specified at the route level. Mime type detection based on the Accept header is planned, it is not currently supported.","redirect-response#Redirect Response":"If the API needs to redirect the client to another location after a POST/PUT/DELETE operation, the handler function can return an ace.Redirect structure. The fields in this structure are:\nProperty Optional Type Default Notes url false string The url to redirect to code true int 303 The HTTP status code, 303 or 302 For example, this code does a 303 redirect after a POST API, which provides handling for update requests.\napp.stardef create_game(req): level = req.Form[\"level\"] ret = http.post(SERVICE_URL + \"/api/create_game/\" + level[0]) return ace.redirect(req.AppPath + \"/game/\" + ret.json()[\"GameId\"]) ","response-data#Response Data":"The Response from the handler function is passed to the template to be converted to HTML. The handler response is accessible through .Data, or $Data if in another scope. Any python object can be used as the return value. Using a dictionary is recommended, so that error handling is easier. Adding a Error key in the response dict can indicate to the template that an error condition needs to be handled.\nFor example, a handler like\napp.stardef handler(req): name = req.Query.get(\"name\") if name: return {\"Name\": name[0], \"Error\": None} else: return {\"Error\": \"Name not specified\", \"Name\": None} app = ace.app(\"test\", routes = [ace.html(\"/\")]) allows the template to handle the error by doing\n{{block \"clace_body\" .}} {{if .Data.Error}} \u003cdiv style=\"color: red\"\u003e{{.Data.Error}}\u003c/div\u003e {{else}} Hi {{.Data.Name}} {{end}} {{end}} "},"title":"Response"},"/docs/app/routing/":{"data":{"":"The request routing layer in Clace is built on top of the chi routing library. API requests (JSON or text), HTML requests and proxied requests are supported. For HTML requests, the routing is built for hypermedia exchange, so all routes are defined in terms of pages and fragments within the pages. This grouping of requests helps make it clear which API does what and provide an easy mechanism to deal with partial HTMX driven requests and full page refreshes. Simpler application might have one page with some interactions within that. Larger applications can be composed of multiple pages, each page having some interactive fragments.","api-route#API Route":"An API route defines a route which returns JSON (default) or plain text response. The handler response is converted to the desired format and returned. The parameters for ace.api are:\nProperty Optional Type Default Notes path False string The route, should start with a / handler True function handler (if defined) The handler function to use for the route method True string GET The HTTP method type: GET,POST,PUT,DELETE etc, for example ace.GET type True string JSON The response type, ace.JSON or ace.TEXT For example\napp.star def handler(req): return {\"a\": 1} app = ace.app(\"api\", routes = [ ace.api(\"/myapi\") ] ) A GET request to /myapi endpoint will return JSON {\"a\": 1}.","fragment#Fragment":"The fragments array in the html page definition defines the API interactions within the page. The parameters for ace.Fragment are:\nProperty Optional Type Default Notes path False string The route, should not start with a / partial True string Inherited from page The template to use for partial requests handler True function Inherited from page The handler function to use for the route method True string GET The HTTP method type: GET,POST,PUT,DELETE etc, for example ace.GET ℹ️ partial and handler are inherited from the page level, unless overridden for the fragment. For example, in this page definition\napp.starace.html(\"/game/{game_id}\", full=\"game.go.html\", partial=\"game_info_tmpl\", handler=game_handler, fragments=[ ace.fragment( \"submit\", method=ace.POST, handler=lambda req: post_game_update(req, \"submit\")), ace.fragment( \"refresh\", partial=\"refresh_tmpl\") ] ) there are three API’s defined:\nGET /game/{game_id} : game_handler is the handler function, full page request returns game.go.html, partial HTMX request returns game_info_tmpl template. POST /game/{game_id}/submit : The handler is a lambda function. The game_info_tmpl template partial is inherited from the page as the response for the POST. GET /game/{game_id}/refresh : game_handler is inherited from the page. For full page, it returns the game.go.html response. For partial HTMX requests, refresh_tmpl template is returned. ","html-route#HTML Route":"An HTML route defined using ace.html defines the properties for a HTML page. The response is HTML text. The data returned by the handler function is passed to the template. If the handler returns a ace.response or ace.redirect, then that takes effect, otherwise the template is rendered.\nAn HTML route can have fragments defined within it. These are sub-apis which used for hypermedia driven interactions from the main page.\nThe parameters for ace.html are:\nProperty Optional Type Default Notes path False string The route, should start with a / full True string index.go.html if custom layout, else index_gen.go.html The template to use for full page requests partial True string None The template to use for partial page requests handler True function handler (if defined) The handler function to use for the route fragments True ace.fragment[] [] The fragment array method True string GET The HTTP method type: GET,POST,PUT,DELETE etc, for example ace.GET ","notes#Notes":" For HTMX requests, the partial template is used. For regular requests, the page level full template is used If there is a function called handler defined, that is the default handler function for all API’s For non-HTMX update requests (POST/PUT/DELETE), the Post-Redirect-Get pattern is automatically implemented by redirecting to the location pointed to by the Referer header. ","proxy-route#Proxy Route":"A Proxy route defines a route which has to be proxied to another service. All API calls under that route are proxied (all methods and all sub-routes). Websocket connections are also proxied. Proxy uses a plugin based config, the app has to be authorized to do the proxying. The parameters for ace.Proxy are:\nProperty Optional Type Default Notes path False string The route, should start with a / config False ProxyConfig The proxy configuration to use The proxy configuration proxy.config has the options:\nProperty Optional Type Default Notes url False string The url to forward the requests to strip_path True string Additional path components to strip from the request path. The app installation path is always stripped. preserve_host False boolean Whether to preserve the Host header. Default false, the Host header is set to the target host value For example, an app which forwards requests to www.google.com is\napp.starload(\"proxy.in\", \"proxy\") app = ace.app(\"Proxy\", routes=[ ace.proxy(\"/\", proxy.config(\"https://www.google.com\")), ], permissions=[ ace.permission(\"proxy.in\", \"config\", [\"https://www.google.com\"]), ], ) The plugin is authorized to allow proxying to https://www.google.com. Any request to the app will be forwarded after stripping the app path. So if app is installed at /test, a request to /test/abc/def will be forwarded to /abc/def. In addition, if the proxy is configured with strip_path=\"/abc\", then the request will be sent to /def.\nIf proxying is enabled for / route, then /static file serving is disabled for the app since requests to static path are also forwarded to the upstream service. /static_root serving is available and overrides the proxy config.\nSee proxy plugin for details about the proxy config.\n⚠️ Note: If the upstream service service uses relative paths, then all requests are automatically proxied. If the service uses absolute paths, then it better that the app is installed at the root path, like example.com: instead of example.com:/test. If the service uses absolute path including the domain name, then the client will see the absolute path and those requests will not come through the proxy. The HTML body is not rewritten by Clace to rewrite path references. The upstream service needs to use relative paths to ensure that all requests come through Clace. ","request-flow#Request Flow":"The API flow is\nThe API is first sent to the matching app Within the app, the API is routed based on the routes defined If there is a handler defined for the matched route, the handler function is called with the request as argument The response template is invoked, with an input map containing a Data property as returned by the handler function If the API type is set to json, the handler response is directly returned, with no template being used If automatic error handling is enabled (error_handler is defined), then the error handler function is called if there is a error during the handler invocation. The error handler does the response processing, the templates defined in the route are not used. ","routes#Routes":"The app contains an routes array, which defines all the routes for the app. For example, the app definition\napp.starapp = ace.app(\"hello1\", routes = [ ace.html(\"/\"), ace.html(\"/help\", \"help.go.html\") ] ) defines two routes. / routes to the default index page, /help routes to the help page. Routes can be of three types: HTML, API and Proxy."},"title":"Routing"},"/docs/applications/":{"data":{"":"Details on how to install and manage Clace apps.\nOverviewOverview of how Clace apps are installed and managed RoutingDetails about how API routing is done for Clace app App LifecycleVarious types of apps: dev, prod, staging and preview; app promotion App SecurityApp security and sandboxing details Audit EventsAuditing and viewing events "},"title":"Managing Apps"},"/docs/applications/appsecurity/":{"data":{"":"Clace applications run in a sandbox environment with no direct access to the system or Clace service. All access is through plugins. When an application is installed, the admin can audit the application for what access is being requested. Only the permissions which are approved are allowed at runtime.","roadmap#Roadmap":"The following enhancements are planned for the security model\nFor the secrets management feature, the app does not have direct access to the secret value. Apps can access any secret from the secret manager currently. An improvement planned to to allow restricting apps to specific secrets. ","sample-application#Sample Application":"As an example, the disk usage analysis app requires two permissions\napp.starapp = ace.app(\"Disk Usage\", routes=[ace.html(\"/\", partial=\"du_table_block\")], permissions=[ ace.permission(\"exec.in\", \"run\", [\"du\"]), ace.permission(\"exec.in\", \"run\", [\"readlink\"]) ], style=ace.style(\"https://unpkg.com/mvp.css@1.14.0/mvp.css\"), ) It requests permission to use the exec.in plugin to run two CLI commands, first being du and other being readlink. When installing the app\n$ clace app create ./examples/disk_usage/ /utils/disk_usage App audit results /utils/disk_usage : app2WPQHwr5ZpKELqh0TvP5YMSnbab Plugins : exec.in Permissions: exec.in.run [du] exec.in.run [readlink] App created. Permissions need to be approved an audit report is shown with these requests. To approve the requested permissions, the admin can do\n$ clace app approve /utils/disk_usage App audit: /utils/disk_usage Plugins : exec.in Permissions: exec.in.run [du] exec.in.run [readlink] App permissions have been approved. The approval can be done during the app create itself, in that case the app is installed and approved immediately. None of the plugin code runs during the app creation, even for calls at the global scope. If the audit report does not match expectations, the app can be deleted.\n$ clace app create --approve ./examples/disk_usage/ /utils/disk_usage App audit results /utils/disk_usage : app2WPQpws6C1mWb6BujYGOdWMnF1C Plugins : exec.in Permissions: exec.in.run [du] exec.in.run [readlink] App created. Permissions have been approved $ clace app delete /utils/disk_usage App deleted /utils/disk_usage Once the app is created, if the application code is updated to change the line from\napp.star ret = exec.run(\"readlink\", [\"-f\", current], process_partial=True) to\napp.star ret = exec.run(\"rm\", [\"-f\", current], process_partial=True) The app will fail at runtime with an error like\napp /utils/disk_usage is not permitted to call exec.in.run with argument 0 having value \"rm\", expected \"readlink\". Update the app or audit and approve permissions The app cannot be run until either the code change is reverted or the admin approves the new call to rm.","security-model#Security Model":"The security model used by Clace is:\nThe application code written in Starlark(python) and HTML templates is untrusted. The Clace service and plugin code (in Go) are trusted. The admin can audit and approve the access required by the untrusted application code when the app is being installed. After installation, further application code updates do not require any further audit, as long as no new permissions are required. If the updated app code requires any new permission, the new plugin call will fail at runtime with a permission error. The trust boundary is about what the application can do in the backend. The frontend code is sandboxed by the browser, there is no additional auditing implemented for the frontend code.\nFor apps which make calls to external programs (using exec plugin) and containerized apps, the external program runs as usual. The Clace security model applies for Starlark apps. For other apps, the security model allows control on which program to run and what args to pass and which container to use. But there is no restriction on what the external program or container itself can do.","usecases#Usecases":"This security model allows for the following:\nUsers can download applications and run on their machine, without worrying about what operations the app can do on their system outside the audited permissions. Operations teams can install and approve applications. Further application updates can be handled by the development team, without requiring the operational admins to verify the updated code. As long as the application works within the originally defined permission boundary, application updates will continue to work. Application developers can use LLM powered automated code generation tools without worrying about the side-effects of the code. If the generated code tries to perform any operation not previously approved, it will fail. "},"title":"Application Security"},"/docs/applications/audit/":{"data":{"":"Clace automatically creates audit events for all operations. See demo for a demo of events viewing.","configuration#Configuration":"The configurable options related to audit events are:\napp_config.audit.redact_url: Set to true to redact the API path for HTTP events. By default, the API path, except for query string, is logged app_config.audit.skip_http_events: Set to true to skip HTTP event logging The app config options can be set globally in the clace.toml. It can also be set individually for an app by setting the app metadata. For example,\nclace app update-metadata conf --promote 'audit.redact_url=true' /myapp The retention for audit events is configurable globally. The config settings in clace.toml are:\nsystem.http_event_retention_days : Number of days to retain http events, default 90 system.non_http_event_retention_days : Number of days to retain non-http events, default 180 ","custom-events#Custom Events":"HTTP, System and Action events are generated automatically. Apps can also define custom events. To add a custom event, in a handler function, add a call to ace.audit. The parameters for ace.audit are:\nProperty Optional Type Default Notes operation false string The operation type to log target false string The target the operation is being done on detail true string Detailed info about the event For example, the dictionary app does:\nace.audit(\"word_lookup\", args.word) This will enable searching the audit events (using the Viewer app) for all operation of type “word_lookup”.\nOnly the last call to ace.audit from a handler function is logged.","event-viewer#Event Viewer":"Events can be viewed by admin using the Event Viewer app code: demo. To install the app on your instance, run\nclace app create --approve github.com/claceio/apps/clace/audit_viewer /events The event viewer shows events for all apps. This app should be installed for access by admins only.","introduction#Introduction":"All operations against the Clace server are automatically logged in a database. The default location for this database is $CL_HOME/metadata/clace_audit.db. This can be configured by setting the property\nclace.toml[metadata] audit_db_connection = \"sqlite:$CL_HOME/metadata/clace_audit.db\" The events which are logged are\nAll HTTP request except GET, HEAD and OPTIONS System events, like app updates and any metadata changes Action invocations (suggest, validate and exec) Custom events, as defined in app code "},"title":"Audit Events"},"/docs/applications/lifecycle/":{"data":{"":"","application-types#Application Types":"A Clace application can be one of four types:\nDevelopment Apps : Used for developing apps, supports live reload from code change on disk. Production Apps : For production use. Can be created from git hosted source or from sources on local disk. Staging Apps : For reviewing code and config changes before they are pushed to prod. Every prod app has one staging app. Preview Apps : For creating a review environment for code changes, useful as part of code review. ","development-apps#Development Apps":"Development mode apps are used for developing or updating Clace apps. The source for these apps has to be on local disk, it cannot be git. Any code or config changes are live reloaded immediately for dev apps. To create a dev mode app, add the --dev option to the app create command. For example,\nclace app create --dev --approve /home/user/mycode /myapp ","github-reload#GitHub Reload":"The rules for fetching source code from local disk and GitHub are:\nIf the source url starts with http://, https:// or github.com, the source is assumed to be from a github API endpoint. Otherwise the source is assumed to be local disk on the Clace server. If Clace client and server are on different machines and local disk is being used, the code needs to be copied to the server node first. For GitHub source, the format is https://domain_name/org_name/repo_name/sub/folder, like github.com/claceio/clace/examples/disk_usage. The sub_folder should contain the app.star config file. During app create and app reload, the commit id takes precedence over the branch name if both are specified. During app reload, if no branch and commit are specified, the newest code is checked out from the current branch. main is used as current branch if no branch was previously specified for the app. ","preview-apps#Preview Apps":"Preview allows the creation of any number of linked preview apps for a main app. This is supported for apps created from GitHub source. The commit id to use needs to be specified when creating the preview. For example,\nclace preview create 49182d4ca1cacbd8e3463a77c2174a6da1fb66c9 /myapp creates an app accessible at /myapp_cl_preview_49182d4ca1cacbd8e3463a77c2174a6da1fb66c9 which runs the app code in the specified commit id.\nPreview apps cannot be changed once they are created. If preview app requires new permissions, add the --approve option to the preview create command.","production-apps#Production Apps":"Without the --dev options, apps are created as production apps by default. Production apps can be created from source on GitHub or from local disk. In either case, the source code for the app is uploaded to the Clace metadata database. For example:\nclace app create --approve /home/user/mycode example.com:/ creates an production app. After app creation, the original source location is not read, until a app reload operation is done to update the sources. The source folder /home/user/mycode can be deleted if reload is not required, since the sources are present in the Clace metadata database. Every production app automatically has one staging app associated with it.","promoting-changes#Promoting Changes":"When there are code changes, running app reload will update the staging environment.\nclace app reload example.com:/ Reloaded apps: example.com:/_cl_stage 1 app(s) reloaded, 0 app(s) approved, 0 app(s) promoted. The staging app is version 2 now, prod app is still at version 1.\nclace app list -i Id Type Version GitCommit GitBranch Domain:Path SourceUrl app_prd_2aMvX3fc9fH18n6i2Jew0tNxnky PROD* 1 example.com:/ /home/user/mycode app_stg_2aMvX3fc9fH18n6i2Jew0tNxnky STG 2 example.com:/_cl_stage /home/user/mycode At this point, going to the url example.com:/_cl_stage will show the updated code while example.com:/ has not been updated.\nℹ️ The * next to PROD indicates that there are staged changes waiting to be promoted to PROD. To promote the changes to prod, run app promote\nclace app promote example.com:/ Promoting example.com:/ 1 app(s) promoted. The prod app is at the same version as the staging app now\nclace app list -i Id Type Version GitCommit GitBranch Domain:Path SourceUrl app_prd_2aMvX3fc9fH18n6i2Jew0tNxnky PROD 2 example.com:/ /home/user/mycode app_stg_2aMvX3fc9fH18n6i2Jew0tNxnky STG 2 example.com:/_cl_stage /home/user/mycode If the application code change requires new permissions, the reload operation will fail unless --approve is added.\nTo do the reload, approval and promotion is one step, do clace app reload --approve --promote example.com:/.","staging-apps#Staging Apps":"Staging apps are created for each production app. The purpose of staging app is to be able to verify config and code changes before they are made live in the prod app. For example, after the previous app create command, a call to app list with the --internal option will show two apps:\nclace app list --internal Id Type Version GitCommit GitBranch Domain:Path SourceUrl app_prd_2aMvX3fc9fH18n6i2Jew0tNxnky PROD 1 example.com:/ /home/user/mycode app_stg_2aMvX3fc9fH18n6i2Jew0tNxnky STG 1 example.com:/_cl_stage /home/user/mycode The second app is the staging app for the first. app list shows only the main apps by default, the --internal option makes it show the linked apps.\nThe staging app url is available by suffixing _cl_stage at the end of the app path. So for an app at https://example.com/, the staging url is https://example.com/cl_stage. For an app at https://example.com/utils/app1, the staging app url is https://example.com/utils/app1_cl_stage.","write-mode-access#Write Mode Access":"Staging and Preview apps have read only access by default to plugin APIs. This means that when they make calls to plugin APIs, only APIs defined as READ by the plugin are permitted. The HTTP plugin defines GET/OPTIONS/HEAD requests as READ type, POST/PUT/DELETE are defined as WRITE. For the CLI Exec plugin, the run API is defined as WRITE since the CLI command run might do write operations.\nFor cases where the plugin defines an API as Write, the app permission can overwrite the default type and define the operation to be a READ operation. For example, the disk_usage app runs the du command, which is a read operation. The app config defines the run plugin call as type=\"READ\", over-riding the default WRITE type defined in the plugin. If no type is specified in the permission, the type defined in the plugin takes effect.\nStaging and Preview apps are allowed only READ calls by default, even if the app permissions allow WRITE operations. To allow stage apps access to WRITE operations, run clace app update-settings stage-write-access true all. Change all to the desired app glob pattern.\nTo allow preview apps access to WRITE operation, run clace app update-settings preview-write-access true example.com:/. This changes the existing preview apps and any new preview apps created for example.com:/ to allow write operations, if the permissions have been approved."},"title":"Application Lifecycle"},"/docs/applications/overview/":{"data":{"":"The various commands for managing Clace apps are\n$ clace NAME: clace - Clace client and server https://clace.io/ USAGE: clace [global options] command [command options] [arguments...] COMMANDS: server Manage the Clace server app Manage Clace apps preview Manage Clace preview apps account Manage Clace accounts param Manage app parameter values version Manage app versions app-webhook Manage app level webhooks password Generate a password bcrypt config entry help, h Shows a list of commands or help for one command $ clace app NAME: clace app - Manage Clace apps USAGE: clace app command [command options] [arguments...] COMMANDS: create Create a new app list List apps delete Delete an app approve Approve app permissions reload Reload the app source code promote Promote the app from staging to production update-settings Update Clace apps settings. Settings changes are NOT staged, they apply immediately to matched stage, prod and preview apps. update-metadata Update Clace app metadata. Metadata updates are staged and have to be promoted to prod. Use \"clace param\" to update app parameter metadata. help, h Shows a list of commands or help for one command The app management subcommands are under the app command. The preview command manages preview app for an app and version command manages versions for an app. The account command is for managing accounts for an app.","app-authentication#App Authentication":"By default, apps are created with the system authentication type. System auth uses admin as the username. The password is displayed on the screen during the initial setup of the Clace server config.\nTo change app to be un-authenticated, add --auth none to the app create command. After an app is created, the auth type can be changed by running app update-settings auth none /myapp. OAuth based authentication is also supported, see authentication for details.\n⚠️ Changes done to the app settings using the app update-settings command are not staged or versioned, they apply immediately to the stage/prod/preview apps. App settings are fundamental properties of the app, like what authentication type to use, what git auth key to use etc.\nAll other changes done to app metadata using app update-metadata, app reload, param update, account link command, (like account linking, permission approval and code reload) are staged before deployment. Use the --promote option on the change to promote the change immediately when applying it on the staging app. Use app promote command to promote later. When a promotion is done, all currently staged changes for that app are promoted, not just the most recent change. After promote, the prod app is exactly same as staging app.","app-listing#App Listing":"Use the app list command to list apps. If the --internal option is used, then the internal staging and preview apps for each matched prod app are also listed.\n$ clace app list Id Type Version Auth GitInfo Domain:Path SourceUrl app_prd_2d3kCRk43FOuIGy979NbBWakRMm PROD 3 NONE main:9ce5e1adfc28983f7894 memory.demo.clace.io:/ github.com/claceio/clace/examples/memory_usage/ app_prd_2d3kCZ28w6VIzoXMsT9yldwijxL PROD 3 NONE main:9ce5e1adfc28983f7894 cowbull.co:/ github.com/claceio/clace/examples/cowbull app_prd_2d3kCjmw8ldQ2LaOd0CLmWXDApq PROD 3 NONE main:9ce5e1adfc28983f7894 / github.com/claceio/clace/examples/demo app_prd_2d3kKVvSsSgUHqtNGZaD95NuLK3 PROD 3 NONE main:9ce5e1adfc28983f7894 du.demo.clace.io:/ github.com/claceio/clace/examples/disk_usage/ app_prd_2d6KcZmNwHIB8cSzNCotqBHpeje PROD 5 NONE main:ed7545ae739dfe85140a utils.demo.clace.io:/bookmarks github.com/claceio/apps/utils/bookmarks $ clace app list -i Id Type Version Auth GitInfo Domain:Path SourceUrl app_prd_2d3kCRk43FOuIGy979NbBWakRMm PROD 3 NONE main:9ce5e1adfc28983f7894 memory.demo.clace.io:/ github.com/claceio/clace/examples/memory_usage/ app_stg_2d3kCRk43FOuIGy979NbBWakRMm STG 3 NONE main:9ce5e1adfc28983f7894 memory.demo.clace.io:/_cl_stage github.com/claceio/clace/examples/memory_usage/ app_prd_2d3kCZ28w6VIzoXMsT9yldwijxL PROD 3 NONE main:9ce5e1adfc28983f7894 cowbull.co:/ github.com/claceio/clace/examples/cowbull app_stg_2d3kCZ28w6VIzoXMsT9yldwijxL STG 3 NONE main:9ce5e1adfc28983f7894 cowbull.co:/_cl_stage github.com/claceio/clace/examples/cowbull app_prd_2d3kCjmw8ldQ2LaOd0CLmWXDApq PROD 3 NONE main:9ce5e1adfc28983f7894 / github.com/claceio/clace/examples/demo app_stg_2d3kCjmw8ldQ2LaOd0CLmWXDApq STG 3 NONE main:9ce5e1adfc28983f7894 /_cl_stage github.com/claceio/clace/examples/demo app_prd_2d3kKVvSsSgUHqtNGZaD95NuLK3 PROD 3 NONE main:9ce5e1adfc28983f7894 du.demo.clace.io:/ github.com/claceio/clace/examples/disk_usage/ app_stg_2d3kKVvSsSgUHqtNGZaD95NuLK3 STG 3 NONE main:9ce5e1adfc28983f7894 du.demo.clace.io:/_cl_stage github.com/claceio/clace/examples/disk_usage/ app_prd_2d6KcZmNwHIB8cSzNCotqBHpeje PROD 5 NONE main:ed7545ae739dfe85140a utils.demo.clace.io:/bookmarks github.com/claceio/apps/utils/bookmarks app_stg_2d6KcZmNwHIB8cSzNCotqBHpeje STG 5 NONE main:ed7545ae739dfe85140a utils.demo.clace.io:/bookmarks_cl_stage github.com/claceio/apps/utils/bookmarks Use the version list command to list versions for particular apps. This command works on prod app or staging app specifically.\n$ clace version list utils.demo.clace.io:/bookmarks_cl_stage Active Version Previous CreateTime GitCommit GitMessage 1 0 2024-03-01 19:59:27 +0000 UTC 86385ff67deab288c362 Updated bookmarks app 2 1 2024-03-01 20:00:20 +0000 UTC 86385ff67deab288c362 Updated bookmarks app 3 2 2024-03-01 20:16:08 +0000 UTC 86385ff67deab288c362 Updated bookmarks app 4 3 2024-03-02 00:12:28 +0000 UTC 8080fcfe6be832a1f6f5 Update styling for bookmarks app =====\u003e 5 4 2024-03-02 00:23:35 +0000 UTC ed7545ae739dfe85140a Update styling for bookmarks app $ clace version list utils.demo.clace.io:/bookmarks Active Version Previous CreateTime GitCommit GitMessage 1 0 2024-03-01 19:59:27 +0000 UTC 86385ff67deab288c362 Updated bookmarks app 2 1 2024-03-01 20:00:20 +0000 UTC 86385ff67deab288c362 Updated bookmarks app =====\u003e 5 2 2024-03-02 00:23:35 +0000 UTC ed7545ae739dfe85140a Update styling for bookmarks app The version switch command can be used to switch versions, up or down or to particular version. The version revert command can be used to revert the last change. app promote makes the prod app run the same version as the current staging app.\nIn the above listing, the staging app has five versions. Three of those (1,2 and 5) were promoted to prod. version switch previous utils.demo.clace.io:/bookmarks_cl_stage will change the stage app to version 4. version switch previous utils.demo.clace.io:/bookmarks will change the prod app to version 2. After that, app promote utils.demo.clace.io:/bookmarks will change prod to also be at version 4, same as stage. The version switch command accepts previous, next and actual version number as version to switch to.\nA star, like PROD* in the app list output indicates that there are staged changes waiting to be promoted. That will show up any time the prod app is at a different version than the stage app.","app-management#App Management":"All the app management subcommands except create take a glob pattern, so multiple apps can be updated using one command. Use the --dry-run option with any update CLI call to verify if the options specified are correct before the actual run. No database changes are committed during dry-run and no changes are done in memory.","glob-pattern#Glob Pattern":"The reload/delete/promote/approve/list/update commands accept a glob pattern. example.com:** will match apps under the example.com domain. *:** will match all apps in all domains, all is a shortcut for this. When using glob patterns, place the pattern inside double-quotes to avoid issues with shell star expansion. For example, \"example.com:**\"\nThe default for app list command is to list all apps. All other commands require an glob pattern to be specified explicitly.\nWhen multiple apps are being updated, if any one app fails, the whole operation is rolled back. This allows for atomic updates across multiple applications."},"title":"App Management Overview"},"/docs/applications/routing/":{"data":{"":"","creating-applications#Creating Applications":"When an app is being created, a path and an optional domain need to be specified. For the above example scenario:\nNo new apps can be created for example.com domain, since App C is installed at the root path. For the default domain, /utils/appA and /appB are taken, other paths are available. New apps can be created under any path, including root path, for new domains and subdomains, like test.example.com. ","notes#Notes":" The domain specified for the app is used only for routing requests. The user has to ensure that the actual DNS routing is done properly outside of Clace for the API calls to land on the Clace server. Using wildcard DNS entries will reduce the configuration required in the DNS service. So if *.example.com points to the IP address of the Clace service, any domain based routing done in Clace will work with no further DNS configuration being required. The automated certificates created by Clace will be domain level certificates, wildcard certificates are not currently created. /_clace/ API path at the root level and /_clace_app/ within an app path are reserved paths, they are used by the Clace internal APIs, requests will not reach the app. This applies for all domains. _cl_ is reserved for use for internal apps, so app path last component cannot have _cl_. ","request-routing#Request Routing":"The Clace server can host multiple applications simultaneously. Each application gets a dedicated path, the application can use all sub-paths within that without conflicts with other applications.\nWhen an application is created, a path needs to be specified and a domain can be optionally specified. Routing for application requests is done based on domain and path. The domain is the namespace within which the paths are resolved. If no domain is specified for the app, it is installed for the default domain. Default domain can be set using the system.default_domain config property (default value is localhost).\nConsider this scenario:\nApp A is installed at /utils/appA App B is installed at /appB App C is installed at the root level / for domain example.com, example.com:/ For the above scenario:\nRequests to /utils/appA/* for the default domain go to app A Requests to /appB/* for the default domain go to App B. Requests to example.com for any path will always go to App C, since it is installed at the root path. "},"title":"Application Routing"},"/docs/configuration/":{"data":{"":"Most configuration options specified in the following sections are for the Clace server. The Clace client CLI, which talks with the Clace server using unix domain sockets, uses a small subset of the config properties. If the Clace client runs on the same machine as the server, then the same config file can be used for both. See here for details.\nOverviewConfiguration overview, CL_HOME env Ports and CertificatesSetting up ports, TLS certs, certificate signing SecurityAdmin account, admin API access, github repo security App authenticationApp authentication using admin account, OAuth account config Secrets ManagementSecrets management, with AWS secrets manager, env, vault and properties file "},"title":"Configuration"},"/docs/configuration/authentication/":{"data":{"":"By default, apps are created with the system authentication type. The system auth uses admin as the username. The password is displayed on the screen during the initial setup of the Clace server config.\nApps can also be changed to have no authentication, making them publicly accessible. To change app to be un-authenticated, add --auth none to the app create command. After an app is created, the auth type can be changed by running app update-settings auth none /myapp.","callback-url#Callback Url":"To enable any Oauth provider, the callback url domain has to be specified in the server config. Add\nclace.toml[security] callback_url = \"https://example.com:25223\" in the clace.toml. In the OAuth account, for an entry github_test, the callback url to use will be https://example.com:25223/_clace/auth/github_test/callback.\nThe format for the callback url to use is \u003cCALLBACK_URL\u003e/_clace/auth/\u003cPROVIDER_ENTRY_NAME\u003e/callback. The callback url has to exactly match this format.","client-cert-authentication-mtls#Client Cert Authentication (mTLS)":"Apps can be updated to use mutual TLS authentication. To enable this, first set disable_client_certs to false in the https section. Add a client_auth config entry in server config with the CA certificate to verify against. Multiple entries can be added, the entry name should be cert or should start with cert_. For example\nclace.toml[https] disable_client_certs = false [client_auth.cert_test1] ca_cert_file=\"/data/certs/ca1.crt\" [client_auth.cert_test2] ca_cert_file=\"/data/certs/ca2.crt\" defines two client_auth configs: cert_test1 using ca1.crt and cert_test2 using ca2.crt. Apps can be updated to use this auth config by running app update-settings auth cert_test1 /myapp or app update-settings auth cert_test2 /myapp.\nAny API call to the app has to pass the client certificates. Using curl, the call would look like:\ncurl -k --cert client.crt --key client.key https://localhost:25223/myapp If the client cert has been signed with the root CA defined in /data/certs/ca1.crt, the API call will succeed. Otherwise it fails. HTTP requests are not allowed when client cert authentication is used.","default-authentication-type#Default Authentication Type":"Any app when created uses the default auth type configured for the server. system is the default. To change this, add\nclace.toml[security] app_default_auth_type = \"github_prod\" assuming there is a github_prod oauth config.\nAny new app created will use this as the auth unless overridden in the app create call or using app update.","oauth-authentication#OAuth Authentication":"OAuth based authentication is supported for the following providers:\ngithub google digitalocean bitbucket amazon azuread microsoftonline gitlab auth0 okta oidc The configuration format for each is\nclace.toml[auth.github_test] key = \"abcdefgh\" secret = \"mysecret\" Here, the auth config entry name is github_test. The entry name can be one of the supported providers, or a supported provider name followed by a _ and a qualifier. The provider name is case sensitive. So github, google, github_prod, google_my_org etc are valid config names. github-test and my_org_google are not valid.\nThe server clace.toml can have multiple auth configs defined. One of them can be set to be the default using app_default_auth_type config. Apps can be configured to use one of system or none or a valid auth config name as the auth. For example, app 1 can use system and app 2 can use github_test.","oauth-config-details#OAuth Config Details":"The config details depend on the provider type. The key is generally the Client Id and the secret is the Client Secret. For some providers, additional config config entries are supported. These are:\ngoogle: The google provider supports a hosted_domain option. This is the domain name to verify on the user being logged in. For example, this can be set to clace.io. okta: The Okta provider supports the org_url config, the tenant url to verify. auth0: The Auth0 provider supports the domain config. oidc: OIDC supports the discovery_url config property. For all the providers, an optional scopes property is also supported. This is the list of scopes to configure for the OAuth account.\n⚠️ The first time a new provider is added, it is important to manually verify an app, to verify if the required authentication restrictions are in place. For example, with google, any valid google user can login, including gmail.com accounts. The hosted_domain config has to be used to restrict this. The OAuth integration internally uses the goth library, see examples for implementation details."},"title":"App Authentication"},"/docs/configuration/networking/":{"data":{"":"","default-domain#Default Domain":"The config has the default domain set to localhost by default. The default domain is used for any app which is installed without an explicit domain being specified. This can be changed to the required value when configuring the server.\nclace.toml[system] default_domain = \"localhost\" # default domain for apps The list_app app is served at the default domain root level if no app is installed there.\nclace.toml[system] root_serve_list_apps = \"auto\" # \"auto\" means serve list_apps app for default domain, \"disable\" means don't server for any domain, To disable this, set root_serve_list_apps to disable. The list apps app uses the defult authentication as set for the system. If another domain needs to be used, set the value to that.\nThe list_apps app can be installed explicitly from github.com/claceio/apps/clace/list_apps source path. This allows the app to be installed with required auth settings. The listing shows apps which are available unauthenticated and apps which are using the same auth as the one set for the list_apps app.","dev-env-certificates#Dev Env Certificates":"For local dev environment, using the auto generated certs will result in browser warnings when connecting to the HTTPS port. To avoid this, use a tool like mkcert to generate root CA for local env. Install mkcert and then run\nmkcert -install mkcert example.com \"*.example.com\" example.test localhost 127.0.0.1 ::1 cp ./example.com+5.pem $CL_HOME/config/certificates/default.crt cp ./example.com+5-key.pem $CL_HOME/config/certificates/default.key The mkcert generated certificates signed with the local CA will be used after the next Clace server restart.\nFor local env, wildcard DNS will not work without tools like dnsmasq. An easier alternative is to add /etc/hosts entries as required mapping to 127.0.0.1. Using url path routing instead of domain based routing for local env is a convenient option.","enable-automatic-signed-certificate#Enable Automatic Signed Certificate":"Clace uses the certmagic library for fully-managed TLS certificate issuance and renewal for production deployment. Certmagic is disabled by default. To enable, the pre-requisites are:\nThe https config is using 443 as the port number. Running on privileged ports requires additional setup There is an DNS entry created pointing your host name or domain wildcard to the IP address of the host running the Clace server. This has to be done in your DNS provider config. Port 443 is reachable from the public internet. This has to be done in your infrastructure provider network settings. Once the pre-requisites are met, set the service_email config parameter to your email address. This enables certmagic based certificate creation. The config will look like:\nclace.toml# HTTPS port binding related Config [https] host = \"0.0.0.0\" port = 443 enable_cert_lookup = true # enable looking for domain specific certificate files on disk service_email = \"MY_EMAIL@example.com\" # CHANGE to your email address use_staging = true # CHANGE to false for production cert_location = \"$CL_HOME/config/certificates\" storage_location = \"$CL_HOME/run/certmagic\" Test out the certificate creation by sending HTTPS requests to port 443. If the certificate is getting created, change use_staging to false. Let’s Encrypt has strict rate limits, use the staging config to ensure that the pre-requisites are met before using the production config.\nWith this config, certmagic is used to create certificates for all HTTPS requests. Self signed certificates and enable_cert_lookup property are not used when certmagic is enabled.","http#HTTP":"For HTTP requests, by default the Clace service listens on port 25222, on the localhost(127.0.0.1) interface. This means the HTTP port can be accessed from the same machine, it cannot be accessed remotely. To configure this, update the config file\nclace.toml[http] host = \"127.0.0.1\" # bind to localhost by default for HTTP port = 25222 # default port for HTTP to desired values. Port 0 means bind to any available port. Port -1 means disable HTTP access. Use host as 0.0.0.0 to bind to all available interfaces.","https#HTTPS":"For HTTPS requests, the Clace service listens on port 25223 by default, on the any(0.0.0.0) interface. This means the HTTPS port can be accessed from the same machine and also remotely. The various HTTPS config settings are:\nclace.toml# HTTPS port binding related Config [https] host = \"0.0.0.0\" # bind to all interfaces (if port is \u003e= 0) port = 25223 # port for HTTPS enable_cert_lookup = true # enable looking for domain specific certificate files on disk service_email = \"\" # email address for registering with Let's Encrypt. Set a value to enable automatic certs use_staging = true # use Let's Encrypt staging server cert_location = \"$CL_HOME/config/certificates\" # where to look for existing certificate files storage_location = \"$CL_HOME/run/certmagic\" # where to cache dynamically created certificates Port 0 means bind to any available port. Port -1 means disable HTTPS access.\nℹ️ Using the HTTPS port is recommended even for the local environment. HTTP/2 works with HTTPS only. Server Sent Events (SSE) are used by Clace for live reload of dev apps, SSE works best with HTTP/2. Without HTTP/2, there can be connection limit issues with HTTP causing connections from browser to Clace server to hang. ","notes#Notes":" Please provide a valid email address in service_email. This allows you to receive expiration emails and also allows the CA to contact you if required. Start the configuration with staging use_staging = true, change to production config use_staging = false after ensuring that DNS and networking is working fine. If port 0 is used, the service will bind to any available port. Look at the stdout or logs to find the port used. Clients would have to be updated after every server restarted to point to the new port. Only the TLS-ALPN challenge is enabled in Clace. The HTTP and DNS based challenges are not supported currently. If Clace is running behind a load balancer, ensure that the load balancer is doing TLS pass-through. If TLS termination is done in the load balancer, then the automatic certificate management done by Clace through certmagic will not work. ","privileged-ports#Privileged Ports":"On Linux, binding to low ports is disabled for non-root users. To enable binding to port 80 for HTTP and 443 for HTTPS, run the command\nsudo setcap cap_net_bind_service=+ep /path/to/clace_binary This would be required after any new build or update of the Clace binary.","redirect-from-http-to-https#Redirect from HTTP to HTTPS":"To enable automatic redirect from HTTP to HTTPS, add redirect_to_https = true in the http section of the config. Also, change the host to 0.0.0.0. For example,\nclace.toml[http] host = \"0.0.0.0\" port = 80 redirect_to_https = true All requests to the HTTP port will 308 redirect to the HTTPS port.","tls-certificates#TLS Certificates":"In the default configuration, where service_email is empty, certmagic integration is disabled. The certificate handling behavior is:\n$CL_HOME/config/certificates is looked up for a crt and key file in the PEM format matching the domain name as passed to the server. If a matching certificate is found, that is used. If no domain specific certificate is found, then the default certificate default.crt and default.key are looked up. If found, that is used. If default certificate is not found, then a self-signed default certificate is auto created in the certificates folder. The intent is to allow custom certificates to be placed in the certificate folder, which will be used. If not found, a self-signed certificate is created and used. For example, if files example.com.crt and example.com.key are found in the certificates folder, those are used for example.com domain."},"title":"Ports and Certificates"},"/docs/configuration/overview/":{"data":{"":"","clace-client-cli#Clace Client CLI":"By default, the Clace client uses Unix domain sockets to connect to the Clace server. $CL_HOME should point to the same location for server and client. If no changes are done for the server defaults, then the client can connect to the server locally without any other configuration being required. See here for details about the client configuration.","clace-server#Clace Server":"The Clace server picks up its configuration from the config file at startup. All the parameters have default values, specified in the code at clace.default.toml.\nA user-specified config file can be provided. The environment variable CL_CONFIG_FILE is used to locate the config file. If not set, it defaults to $CL_HOME/clace.toml.\nValues in the user specified config take precedence over the default config values in the source code.","home-directory#Home Directory":"The CL_HOME environment variable is used to locate the home directory for the Clace server. If no value is set, this defaults to the directory from which the Clace server was started. This location is used to store:\nThe default config file, $CL_HOME/clace.toml The sqlite database containing the metadata information, default is $CL_HOME/clace.db The logs for the service, under the logs folder. The config folder contains the certificates to be use for TLS. The run folder contains app specific temporary files. "},"title":"Config Overview"},"/docs/configuration/secrets/":{"data":{"":"Clace supports secret management when working with apps. Secrets can be passed to containerized apps through the environment params. Secrets can also be passed to any plugin as argument. For OAuth config, the client secrets can be configured as secret in the config file.","aws-secrets-manager#AWS Secrets Manager":"To enable ASM, add one or more entries in the clace.toml config. The config name should be asm or should start with asm_. For example\nclace.toml[secret.asm] [secret.asm_prod] profile = \"myaccount\" creates two ASM configs. asm uses the default profile and asm_prod uses the myaccount profile. The default config is read from the home directory ~/.aws/config and ~/.aws/credentials as documented in AWS docs. The user id under which the Clace server was started is looked up for the aws config file.\nTo access a secret in app parameters from asm_prod config, use --param MYPARAM='{{secret \"asm_prod\" \"MY_SECRET_KEY\"}}' as the param value.","default-provider#Default Provider":"If the provider name is passed as default or set to empty, a default provider is used. The default provider can be configured in the clace.toml as\nclace.toml[app_config] security.default_secrets_provider = \"env\" The env provider is used by default if it is enabled in the config. The default can be changed per app by setting\nclace app update-metadata conf --promote 'security.default_secrets_provider=\"prop_myfile\"' /myapp ","environment-secrets#Environment Secrets":"Adding a secret provider with the name env or starting with env_, like\nclace.toml[secret.env] enables looking up the Clace server environment for secrets. This can be accessed like --param MYPARAM='{{secret \"env\" \"MY_SECRET_KEY\"}}'. No properties are required in the env provider config. The value of MY_SECRET_KEY in the Clace server env wil be passed as the param.","hashicorp-vault#HashiCorp Vault":"To enable Vault secret provider, add one or more entries in the clace.toml config. The config name should be vault or should start with vault_. For example\nclace.toml[secret.vault_local] address = \"http://127.0.0.1:8200\" token = \"abc\" [secret.vault_prod] address = \"http://myvault.example.com:8200\" token = \"def\" creates two Vault configs. The address and token properties are required.","multiple-keys#Multiple Keys":"If the KEY_NAME is a single string, it is passed as is to the provider. If multiple keys are specified, they are concatenated and passed to the provider. For example, {{secret \"env\" \"ABC\" \"DEF\"}} will get converted to a env lookup for ABC_DEF. The delimiter used depends on the provider. The defaults are:\nASM and Vault : / Env : _ Properties: . The formatter used to concatenate the keys can be customized by setting the keys_printf property. For example,\nclace.toml[secret.prop] file_name = \"/etc/mykeys.properties\" keys_printf = \"%s-%s.%s\" combines {{secret \"prop\" \"ABC\" \"DEF\" \"XYZ\"}} as ABC-DEF.XYZ. This allows the app to work with multiple secret providers without requiring code changes in the app.","plugin-access-to-secrets#Plugin Access to Secrets":"For secrets which are passed to plugins, through app params or plugin arguments, the plugin needs to be authorized to access the secret. The permissions for each plugin are defined in the app definition. For example:\napp.starapp = ace.app(\"test\", routes = [ace.api(\"/\", type=\"TEXT\")], permissions = [ ace.permission(\"exec.in\", \"run\", [\"ls\"], secrets=[[\"c1\", \"c2\"], [\"TESTENV\"]]), ] ) The secrets accessible are specified as a list of list of strings. In this case, the {{secret \"PROVIDER_NAME\" \"c1\" \"c2\"}} and {{secret \"PROVIDER_NAME\" \"TESTENV\"}} calls are allowed. Additional keys are also permitted.","properties-secrets#Properties Secrets":"Secrets can be read from a properties file. The config name should be prop or should start with prop_. To use this, add\nclace.toml[secret.prop_test1] file_name = \"/etc/props.properties\" file_name is a required property.","secrets-usage#Secrets Usage":"Secrets can be accessed using the syntax {{secret \"PROVIDER_NAME\" \"KEY_NAME\"}}. The three contexts in which secrets can be accessed are:\nApp Params : Param values in params.star or in the app metadata definition can access the secrets. Plugin arguments : Secrets can be passed as string arguments in calls to plugin functions. Config file: Secrets are supported in clace.toml config for: For client key and secret in auth config For password in git_auth config For string values in plugin config Secrets are always resolved late. The Starlark code does not get access to the plain text secrets. The secret lookup happens when the call to the plugin API is done. In case of params, the lookup happens when the param is passed to the container.\nFor git_auth config, an example secret usage is\nclace.toml[auth.google_prod] key = \"mykey.apps.googleusercontent.com\" secret = '{{secret \"PROVIDER_NAME\" \"GOOGLE_OAUTH_SECRET\"}}' hosted_domain = \"example.com\" ","supported-providers#Supported Providers":"Clace currently supports AWS Secrets Manager (ASM) and HashiCorp Vault as providers for secrets management. Secrets can also be read from the environment of the Clace server, which can be used in development and testing."},"title":"Secrets Management"},"/docs/configuration/security/":{"data":{"":"The default configuration for the Clace server is:\nApplication management (admin APIs) are accessible over unix domain sockets only (not accessible remotely). Since UDS enforces file permissions checks, no additional authentication is needed for admin APIs. Admin user account is used to access applications, default auth for apps is system The admin user password bcrypt hash has to be added to the server config file, or a random password is generated every time the server is restarted Applications can be changed to not require any authentication, auth can be none or use Oauth2 based auth. There is no user management support in Clace currently. The system account is present by default (which can be disabled) or OAuth based auth can be used. ","admin-account-password#Admin Account Password":"When the Clace server is started, it looks for the entry\nclace.toml[security] admin_password_bcrypt = \"\" # the password bcrypt value in the config file. If the value is undefined or empty, then a random password is generated and is used as the admin password for that server session. The password being used is displayed on the stdout of the server startup. This will change on every restart.\nTo configure a value for the admin user password, use the password helper command:\nclace password to generate a random password. This will print out the password and its bcrypt value to the screen. Save the password in your password manager and add the bcrypt hash to your config file.\nTo use a particular value for the admin password, run:\nclace password --prompt This will prompt for the password and print out the bcrypt hash to add to the config file.","admin-api-access#Admin API Access":"By default, the Clace client uses Unix domain sockets to connect to the Clace server. Admin API calls to manage applications are disabled over HTTP/HTTPS by default. Unix sockets work when the client is on the same machine as the server, the client does not need to pass any credentials to connect over unix sockets.\nTo enable remote API calls, where the client is on a different machine from the server, the server needs to be changed to add the following:\nclace.toml[security] admin_over_tcp = true If running the Clace client from a remote machine, the config options required for the client are:\nclace.tomlserver_uri = \"https://\u003cSERVER_HOST\u003e:25223\" admin_user = \"admin\" [client] admin_password = \"\" # Change to actual password skip_cert_check = false # Change to true if using self-signed certs All other server related config entries are ignored by the Clace client. Note that to connect to a Clace server over HTTP remotely, the server needs to be bound to the all interface(0.0.0.0), see here.\nIf server_uri is set to the https endpoint and the Clace server is running with a self-signed certificate, set skip_cert_check = true in config to disable the TLS certificate check.","application-security#Application Security":"See appsecurity for details about the application level sandboxing.","private-repository-access#Private Repository Access":"The app create and app reload commands can read public GitHub repositories. If the repository is private, to be able to access the repo, the ssh key needs to be specified. In the clace.toml config file, create an entry like:\nclace.toml[git_auth.infoclace] key_file_path = \"/Users/myuser/.ssh/infoclace_rsa\" password = \"\" infoclace is the git auth key name, key_file_path points to the location of a private key file for a user with access to the repository. When running app create, add the --git-auth infoclace option. The private key specified will be used for accessing the repository. app reload command will automatically use the same key as specified during the create.\nTo change the git auth key for an app, run:\nclace app update-settings git-auth newkey /myapp "},"title":"Security"},"/docs/container/":{"data":{"":"Details about developing containerized Clace applications.\nOverviewOverview of a containerized Clace app Container ConfigDetails about the container config and state management "},"title":"Containerized Apps"},"/docs/container/config/":{"data":{"":"The default configuration for the Clace server is defined here. The container related config settings are\n[app_config] # Health check Config container.health_url = \"/\" container.health_attempts_after_startup = 30 container.health_timeout_secs = 5 # Idle Shutdown Config container.idle_shutdown_secs = 180 container.idle_shutdown_dev_apps = false # Status check Config container.status_check_interval_secs = 5 container.status_health_attempts = 3 A health check is done on the container after container is started. If the health check fails 30 times, the container is assumed to be down.\nIn the running state, a status check is done on the app every five seconds. If three of those checks fail, then the container is assumed to be down.\nIf an app does not receive any API request for 180 seconds, the app is assumed to be idle and the container is stopped. The idle shutdown does not apply for dev apps, only for prod mode apps.","changing-config#Changing Config":"The clace.toml can be updated to have a different value for any of the properties. After the server restart, the config change will apply for all apps.\nTo apply the config at the app level, the app metadata can be updated. For example the command\nclace app update-metadata conf --promote container.idle_shutdown_secs=600 /myapp changes the idle timeout for the /myapp app to 600 secs. Without the --promote option, the change will be staged and can be verified on the staging app. App metadata level setting take precedence over the defaults in the clace.toml. Using all as the app name will apply the change for all current apps (but not for any new apps created later).","dockerpodman#Docker/Podman":"The default for the container command to use is\n[system] container_command = \"auto\" auto means that Clace will look for podman in the path. If found, it will use that. Else it will use docker as the container manager command. If the value for container_command is set to any other value, that will be used as the command to use.\nOrbstack implements the Docker CLI interface, so Orbstack also works fine with Clace."},"title":"Container Config"},"/docs/container/overview/":{"data":{"":"Clace builds the image and manages the container lifecycle for containerized apps. Clace fetches the source code, creates the image, starts the container, does health checks on the container and stops the container when idle. Appspecs allow existing source code to be used with Clace with no code changes required.","app-environment-params#App Environment Params":"For containerized apps, all params specified for the app (including ones specified in params.star spec) are passed to the container at runtime as environment parameters. CL_APP_PATH is a special param passed to the container with the app installation path (without the domain name). PORT is also set with the value of the port number the app is expected to bind to within the container.\nFor example, the command\nclace app create --approve --spec python-fasthtml \\ --param APP_MODULE=basic_ws:app \\ https://github.com/AnswerDotAI/fasthtml/examples fasthtmlapp.localhost:/ creates a FastHTML based app. The APP_MODULE env param is passed to the Container and passed to the startup command line in the Containerfile.\nTo update params, run\nclace param update APP_MODULE basic_app:app fasthtmlapp.localhost:/ Param updates are staged, they can be promoted after verification. To delete a param, pass - as the value to the update. Use clace param list to view app params.\nParams can be set to secrets, by setting the value as {{secret \"vault_prod\" \"MY_KEY_NAME\"}}. The secret is resolved when the container is started and the value is passed to the container in its env.\nℹ️ Note: Staged param updates are a powerful mechanism to ensure that config changes do not break your apps. For example, if BUCKET_NAME is a param pointing to a S3 bucket, the param change can be staged. The staging app can be tested to ensure that the new bucket is functional and there are no IAM/key related errors. Once the staging app is working, the app can be promoted. Code changes are easy to test, but config changes can cause env specific errors. Configuration related issues are a common cause of outages during deployment. Clace enables you to avoid such errors. ","app-specs#App Specs":"Clace app specs are defined at https://github.com/claceio/appspecs. Most specs use containers, the proxy spec is an exception.\nThe image spec specifies the image to use. for example\nclace app create --spec image --approve --param image=nginx \\ --param port=80 - nginxapp.localhost:/ downloads the nginx image, starts it and proxies any request to https://nginxapp.localhost:25223 to the nginx container’s port 80. The container is started on the first API call, and it is stopped automatically when there are no API calls for 180 seconds.\nFor all other specs, the Containerfile is defined in the spec. For example, for the python-streamlit spec, the Containerfile is here. Running\nclace app create --spec python-streamlit --branch master \\ --approve github.com/streamlit/streamlit-example /streamlit_app will create an app at https://localhost:25223/streamlit_app. On the first API call to the app, the image is built from the defined spec and the container is started. The python-gradio spec does the same for gradio apps.","app-specs-listing#App Specs Listing":"The specs defined currently are:\nSpec Name Required Params Optional Params Supports Path Routing Notes Example container port : The port number within container, optional if EXPOSE directive is present Depends on app Requires app code to have a Containerfile/Dockerfile image image: The image to use for the container port : The port number within container Depends on app No source url required when creating app, use - as url clace app create --spec image --approve --param image=nginx --param port=80 - nginxapp.localhost:/ proxy url: The url to which requests should be proxied No No source url required when creating app, use - as url clace app create --spec proxy --approve -param url=https://clace.io - proxyapp.localhost:/ python-wsgi APP_MODULE: The module:app for the WSGI app. Defaults to app:app, meaning app in app.py Depends on app Runs Web Server Gateway Interface (WSGI) apps using gunicorn python-asgi APP_MODULE: The module:app for the ASGI app. Defaults to app:app, meaning app in app.py Depends on app Runs Asynchronous Server Gateway Interface (ASGI) apps using uvicorn python-flask port : The port number within container. If EXPOSE directive is present, that is used. Defaults to 5000 Depends on app Runs app using flask dev server python-streamlit app_file : The file name of the streamlit app to run. Default streamlit_app.py Yes clace app create --spec python-streamlit --branch master --approve github.com/streamlit/streamlit-example /streamlit_app python-streamlit-poetry app_file : The file name of the streamlit app to run. Default streamlit_app.py Yes Installs packages using poetry python-fasthtml APP_MODULE: The module:app for the ASGI app. Defaults to app:app, meaning app in app.py Depends on app Runs app using uvicorn clace app create --approve --spec python-fasthtml --param APP_MODULE=basic_ws:app https://github.com/AnswerDotAI/fasthtml/examples fasthtmlapp.localhost:/ python-gradio app_file : The file name of the gradio app to run. Default run.py Yes clace app create --spec python-gradio --approve github.com/gradio-app/gradio/demo/blocks_flag /gradio_app go port : The port number within container MAIN_PACKAGE : The go module to build, default “.”. Pass as a --carg instead of --param.APP_ARGS : Args to pass to the app Depends on app CGO is disabled; go.mod has to be present; app should bind to 0.0.0.0 clace app create --approve --spec go --param port=8080 --param APP_ARGS=\"-addr 0.0.0.0:8080\" --branch master github.com/golang/example/helloserver /goapp ","container-build-args#Container Build Args":"If the Containerfile has an argument, the arg can be passed during the app create. Most python specs have the python version as an argument, For example, https://github.com/claceio/appspecs/blob/a06a59a91d99520e271c6f3df68b6fb8292dbf50/python-fasthtml/Containerfile#L2 sets\nARG PYTHON_VERSION=3.12.5-slim To change this during app creation, pass --carg PYTHON_VERSION=3.11.1. For example,\nclace app create --approve --spec python-fasthtml \\ --param APP_MODULE=basic_ws:app \\ --carg PYTHON_VERSION=3.11.1 \\ https://github.com/AnswerDotAI/fasthtml/examples fasthtmlapp.localhost:/ To update args, run\nclace app update-metadata carg PYTHON_VERSION=3.11.2 fasthtmlapp.localhost:/ Like all metadata updates, arg updates are staged. Pass --promote to promote immediately or run app promote to promote from stage to prod.\nℹ️ Note: The slim images are smaller, but they lack some debugging tools. The regular image can be used during development. ","container-options#Container Options":"To set CPU and memory limits and other options for the container, pass --copt optkey[=optvalue] to the app create command. For example, --copt cpu-shares=1000\nclace app create --approve --spec python-fasthtml \\ --param APP_MODULE=basic_ws:app \\ --copt cpu-shares=1000 \\ https://github.com/AnswerDotAI/fasthtml/examples fasthtmlapp.localhost:/ sets the CPU shares for the container to 1000.\nTo update container options, run\nclace app update-metadata copt cpu-shares=500 fasthtmlapp.localhost:/ Like all metadata updates, option updates are staged. Pass --promote to promote immediately or run app promote to promote from stage to prod.\nℹ️ Note: By default there are no limits set for the containers. That allows for full utilization of system resources. To avoid individual apps from utilizing too much of the system resources, CPU/memory limits can be set. "},"title":"Container Overview"},"/docs/develop/":{"data":{"":"","action-apps#Action Apps":"For use cases where an existing CLI application or API needs to be exposed as a web app, actions provide an easy solution. First, define the parameters to be exposed in the form UI. Create a params.star file with the params. For example,\nparams.starparam(\"dir\", description=\"The directory to list files from\", default=\"/tmp\") The app defines a run handler which runs ls on the specified directory. The output text is returned.\napp.starload (\"exec.in\", \"exec\") def run(dry_run, args): out = exec.run(\"ls\", [\"-Lla\"]) if out.error: return ace.result(out.error) return ace.result(\"File listing for \" + args.dir, out.value) app = ace.app(\"List Files\", actions=[ace.action(\"List Files\", \"/\", run, description=\"Show the ls -a output for specified directory\")], permissions=[ ace.permission(\"exec.in\", \"run\", [\"ls\"]), ], ) The app, when accessed will look as shown below, with the ls command output displayed:\nSee list files code:demo for the above app. See dictionary code:demo for another actions example app which shows different type of reports. Actions has more details on building app actions.","app-lifecycle#App Lifecycle":"The Clace app development lifecycle is:\nCreate a folder for the app, with the app.star file and templates. Start the Clace server. Create an app using clace app create --dev. This runs the app in dev mode. In dev mode, some additional files are generated, with _gen in the file name. CSS dependencies and JavaScript modules are downloaded into the static folder. After the app development is done, the whole app folder can be checked into source control. There is no build step. Create a production app, clace app create, without the --dev. The app is now live. The Clace server can host multiple applications, each application has a dedicated path and optionally a dedicated domain. ","app-parameters#App Parameters":"Having a file params.star in the app source code causes Clace to load the parameters definitions from that file. Parameters are environment values which can be specified during app creation. A sample param definition is\nparams.starparam(\"port\", type=INT, description=\"The port the flask app is listening on (inside the container)\", default=5000) param(\"app_name\", description=\"The name for the app\", default=\"Flask App\") param(\"preserve_host\", type=BOOLEAN, description=\"Whether to preserve the original Host header\", default=False) This is defining three parameters. The type can be one of STRING(default), INT, BOOLEAN, LIST and DICT. The param structure definition is\nProperty Optional Type Default Notes name False string Has to be be a valid starlark keyword type True STRING, INT, BOOLEAN, LISTorDICT STRING The data type default True Type as set for type Zero value for the type description True string The description for the param required True bool True If required is True and default value is not specified, then validation fails display_type True string How ths param should be displayed in the UI. Options are FILE, PASSWORD and TEXTAREA, default is text input. The parameters are available in the app Starlark code, through the param namespace. For example, param.port, param.app_name etc. See https://github.com/claceio/appspecs/blob/main/python-flask/app.star for an example of how this can be used.\nParams are set, during app creation using app create --param port=9000 or using param update port 9000 /myapp. Set value to - to delete the param. Use param list /myapp to list the params.\nFor containerized apps, all params specified for the app (including ones not specified in params.star spec) are passed to the container at runtime as environment parameters. CL_APP_PATH is a special param passed to the container with the app installation path (without the domain name). PORT is also set with the value of the port number the app is expected to bind to within the container.","building-apps-from-spec#Building Apps from Spec":"A spec (specification) can be set for an app. This makes Clace use the spec as a template to specify the app configuration. Use app create --spec python-flask while creating an app or change the spec using app update-metadata spec python-flask /myapp. The spec brings in a set of predefined files. If a file with the same name is already present in the app code, then the spec file is ignored. So if the app code and spec both define a Containerfile, the file from the app code takes precedence. If the app folder contains just app.py\nflaskapp/app.pyfrom flask import Flask app = Flask(__name__) @app.route(\"/\") def hello_world(): return \"\u003cp\u003eHello, World!\u003c/p\u003e\" Creating an app like clace app create --approve --spec python-flask ./flaskapp /myapp will do everything required to fully define the Clace app. If the app has additional python dependencies, add a requirements.txt file in the app source code. By default, only the flask package is installed. The file in the app source takes precedence.\nSee https://github.com/claceio/appspecs for the list of specs. The Clace server build includes these spec by default. Additional specs can de defined by creating a folder $CL_HOME/config/appspecs. Any directory within that is treated as a spec. If the name matches with the predefined ones the spec in the config folder takes precedence. No server restart is required after spec changes. Setting up the server by doing\ncd $CL_HOME/config git clone https://github.com/claceio/appspecs.git ensures that the specs are updated to the latest version. Periodically doing a git pull on this folder refreshes the specs. Instead of cloning the main spec repo, a custom spec repo can also be used similarly. If no custom specs are defined, the specs as bundled in the Clace server build are available.","containerized-app#Containerized App":"A containerized apps needs to have a Containerfile (or Dockerfile) to define how the image is built. The app definition can have\napp.starload(\"proxy.in\", \"proxy\") load(\"container.in\", \"container\") app = ace.app(\"My App\", routes=[ ace.proxy(\"/\", proxy.config(container.URL)) ], container=container.config(container.AUTO), permissions=[ ace.permission(\"proxy.in\", \"config\", [container.URL]), ace.permission(\"container.in\", \"config\", [container.AUTO]) ] ) which completely specifies the app. This is saying that the app is using the container plugin to configure the container and the proxy plugin to proxy all API calls (/ route) to the container url. On the first API call to the app, Clace will build the image, start the container and proxy the API traffic to the appropriate port. No other configuration is required in Starlark. If the container spec does not define the port being exposed, then the container config needs to specify the port number to use. The port number can be parameterized.\nContainerized Apps has more details on building containerized apps.","more-examples#More examples":"There is one disk_usage example here and many in the apps repo. The disk_usage example uses the MVP classless library for styling and shows a basic hypermedia flow. The cowbull game has multiple pages, each page with some dynamic behavior. The cowbull game depends on another service for data persistence, so it is implementing a backend for frontend pattern. For styling, it uses the DaisyUI component library for Tailwind CSS. These two examples work fine with Javascript disabled in the browser, falling back to basic HTML without any HTMX extensions.\nThe memory_usage example uses the d3 library to show a interactive display of the memory usage for processes on the machine. The plot library is automatically imported as a ECMAScript module and the custom javascript code works with a JSON api on the backend. The default in Clace is hypermedia exchange, JSON can be used for data API’s.\nStylingStyling configuration for apps, using CSS and Tailwind JavascriptImporting JavaScript libraries and ESModules TemplatesHTML template handling details ","simple-text-app#Simple Text App":"The hello world app for Clace is an ~/myapp/app.star file containing:\napp.stardef handler(req): return \"hello world\" app = ace.app(\"hello\", routes = [ace.api(\"/\", type=ace.TEXT)] ) Run clace app create --auth=none ~/myapp /hello. After that, the app is available at /hello\n$ curl localhost:25222/hello hello world The default response type is ace.HTML. ace.TEXT and ace.JSON are the other options. The data returned by the handler function is converted to the type format specified in the API.","structure#Structure":"The structure of a Clace application is:\nOne Clace application per folder, static sub-folder contain static assets An app.star Starlark file, defining the application configuration Predefined builtins, accessed through the ace namespace A global called app, created using app = ace.app() call An optional default handler function called handler. Other handlers are referenced in the route config An optional error handler function called error_handler. Defining the error_handler enables automatic error handling An html template file called index.go.html if using custom layout If not using custom layout, an html template block called clace_body defined in any *.go.html file, for example app.go.html "},"title":"Developing Apps"},"/docs/develop/javascript/":{"data":{"":"Clace supports importing JavaScript libraries as JavaScript Modules . To use this feature, add\napp.star libraries=[ace.library(\"d3\", \"7.8.5\")] in the app definition. The fields in the ace.library structure are:\nProperty Optional Type Default Notes name false string The name of the library to import version false string The version of the library args true string[] [] Arguments to pass to esbuild The args array uses the esbuild cli syntax. For example, passing args as [\"--minify\"] will enable minification for the imported module.\nTo directly download a library from a CDN to the static folder, add the url directly. For example,\napp.star libraries=[\"https://unpkg.com/jquery@3.3.1/dist/jquery.min.js\"] The HTMX library and its SSE extension are automatically downloaded.","esbuild-config#Esbuild Config":"Clace includes esbuild, there is no need to install esbuild manually. The Clace server config has the entry\nclace.toml[system] node_path = \"\" The node_path property is used by esbuild, these paths are searched for packages in addition to the node_modules directories in all parent directories. See esbuild docs for details. Paths should be separated with : on Unix and ; on Windows.\nIf you install the npm packages in your home directory, esbuild will pick up those without any additional configuration. Since each Clace project is importing the npm package as a module, you do not need to maintain separate node_modules for each Clace project.\nIf you do not want esbuild to create modules, set the node_path property in the server config to disable. You will have to manually download the module file into the static folder.","javascript-modules#JavaScript Modules":"JavaScript modules (also called ESM or ECMAScript Modules) are a way to import Javascript libraries dynamically, providing a unique namespace for all functions. Modules, once imported, can be used in the client code without requiring any build steps. See jvns.ca and simonwillison.net for notes about this approach. Clace tries to provide an easy interface to modules, converting npm packages to module format so that subsequent code changes do not require any build steps.","notes#Notes":" The version number specified in the ace.library is used to create the file name under static/gen/esm. The actual package version depends on what was install using npm. Ensure that the same version is installed by npm as specified in the library config. Only the minify option for esbuild has been tested with Clace. Other options like chunking the files might not work currently. The JavaScript support is for running JS on the browser. There is no support for running JavaScript on the Clace server, the server runs only Go code and starlark applications. If an library url is specified, that is downloaded directly. There is no need for npm package in that case. ","workflow#Workflow":"The workflow when using modules in Clace for an app in dev mode is:\nEnsure nodejs is installed Using npm, install package you want to use a modules. for example npm install d3 Add the ace.library entry in the app config. Clace will automatically run esbuild and import the package as a module into static/gen/esm Add an importmap in the head section of the html. Like here. \u003cscript type=\"importmap\"\u003e { \"imports\": { \"d3\": \"{{ static \"gen/esm/d3-7.8.5.js\"}}\" } } \u003c/script\u003e Use the library as required in your client. Like here import * as d3 from \"d3\"; Creating the module is a one time step. The generated module can be checked into source code. On a new machine, to make code changes to the app, you do not need nodejs or npm to be installed.\nFor production deployment, no changes are required. Checkout the git repo containing the source code and create a Clace app. Clace will serve the static assets over HTTP/2 with content hash based caching. The assets are compressed for serving, there is no need usually for mimifying the modules."},"title":"JavaScript"},"/docs/develop/styling/":{"data":{"":"Clace supports working with Classless CSS libraries and also with TailwindCSS and DaisyUI. To use this, add the directive\napp.star style=ace.style(\"daisyui\") in the app definition. The fields in the ace.style structure are:\nProperty Optional Type Default Notes library false string The library to use, url to classless library, “tailwindcss” or “daisyui” themes true string[] [] The daisyui themes to include disable_watcher true bool false Whether to disable the tailwind watcher process startup in dev mode light true string emerald The DaisyUI theme to use in light mode for Actions dark true string night The DaisyUI theme to use in dark mode for Actions ","classless-css#Classless CSS":"If the library property is a url, it should point to a publicly accessible style file. The style file is downloaded into the static/gen/css/style.css file. The file is automatically included as part of the clace_gen_import template.\nFor example,\napp.star style=ace.style(\"https://unpkg.com/mvp.css@1.14.0/mvp.css\"), imports the MVP.css library. Since this is classless, no changes are required in the HTML templates.","daisyui#DaisyUI":"To use DaisyUI, in app settings, add\napp.star style=ace.style(\"daisyui\", themes=[\"dark\"]) Change to the preferred theme. DaisyUI is a good option to use to get great default styling for components, with the full flexibility of Tailwind. To use DaisyUI, use the npm version of Tailwind or use this custom version of the standalone CLI with DaisyUI included. Clace takes care of creating the config files. Using the CDN version of DaisyUI or Tailwind is not recommended since that will cause the style files to be large.\nIf using Actions, DaisyUI styles are automatically included. The themes can be customized using the light and dark property.","tailwindcss#TailwindCSS":"To use TailwindCSS, in app settings, add\napp.star style=ace.style(\"tailwindcss\") Tailwind CSS works by scanning the HTML files for class names, generating the corresponding styles and then writing them to a static CSS file. A watcher process is started when an app using Tailwind is loaded in dev mode. The output of the watcher is written to static/gen/css/style.css file. This file is automatically included as part of the clace_gen_import template.\nTo ensure that the tailwind watcher is started, the tailwind CLI needs to be installed manually. The standalone CLI can be used. If using DaisyUI, use this custom build of the standalone CLI with DaisyUI included.\nThe Clace server config file has the following entries:\nclace.toml[system] tailwindcss_command = \"npx tailwindcss\" file_watcher_debounce_millis = 300 tailwindcss_command is the command use to start the watcher. If the standalone version is being used change to\nclace.toml[system] tailwindcss_command = \"/path/to/tailwindcss\" file_watcher_debounce_millis is used to prevent repeated reloads of the application files during dev mode. On slower machine, this value might have to be increased, but setting it too high will cause the reload to be slower."},"title":"Styling"},"/docs/develop/templates/":{"data":{"":"Clace uses Go HTML templates for returning data to the client. See here for an overview of the template syntax. Hugo docs are a good source for an overview of using go templates.\nThe Sprig template library functions are included automatically. Two functions from Sprig which are excluded for security considerations are env and expandenv.\nTwo extra functions static and fileNonEmpty are added for handling static file paths.","app-layout#App Layout":"When using custom layout with custom_layout=True, the app developer has to create the index.go.html file. Add a directive like:\n{{ template \"clace_gen_import\" . }} in the head section to ensure that the auto generated clace_gen_import directives are loaded in the . This will include the style files, HTMX library and the live reload functionality in dev mode.\nIn the default layout mode, the auto generated index_gen.go.html file is used. The app developer has to provide a clace_body block. It can be in any template file, the convention is to use app.go.html. For example:\n{{block \"clace_body\" .}} Data is {{.Data}} {{end}} The .Data binding has the data as returned by the handler function for the route.","filenonempty-function#fileNonEmpty function":"The fileNonEmpty function returns a bool, indicating whether a static file with that non-hashed name is present and is not empty. This can be used to conditionally include style files if present.\nFor example\n{{ if fileNonEmpty \"css/style.css\" }} \u003clink rel=\"stylesheet\" href=\"{{ static \"css/style.css\" }}\" /\u003e {{ end }} checks if the “css/style.css” file is present and not empty. If so, it is linked using the static function, which returns a hashed file name which can be cached aggressively.\n⚠️ The path passed to static and fileNonEmpty functions should not include static, it is automatically added. So use {{ static \"css/style.css\" }}, not {{ static \"static/css/style.css\" }} ","static-function#static function":"This function takes a file name and returns the url for a file in the static folder with a sha256 hash included in the file name. This approach is similar to the hashfs library. If the static folder contains a file file1 with the content file1data, then a call to static \"file\" will return /test/static/file1-ca9e40772ef9119c13100a8258bc38a665a0a1976bf81c96e69a353b6605f5a7, assuming the app is installed at /test.\nThe returned file name has a hash based on the file contents. The file server used by Clace will serve aggressive cache headers Cache-Control: public, max-age=31536000 when this file is referenced by the browser. When the file contents change, the content hash will change and the file name will change. The files on disk are not renamed, only the filesystem used by the Clace server in memory sees the hashed file names.\nThis approach allows for a build-less system with aggressive static asset caching. The usual approach for this requires the static file to be renamed to have the hash value in the file name on disk. This require a build step to do the file renaming. The hashfs approach can avoid the build step. The file hash computation and compression are done once, during app installation in prod mode. There is no runtime penalty for this. In dev mode, the file hashing is done during the api serving.","static-root-files#Static Root Files":"The static folder is used for file which are served under the /static path. Content based hashing is supported for these files.\nFor files which need to be served under the root level, the static_root folder is used. Files in this folder are served at the root path. For example, if an app is installed at example.com: and a robots.txt file needs to be served, a file static_root/robots.txt can be added to the app. This will be automatically served at example.com/robots.txt. Note that the static folder path is stripped from the route name. The file name should not conflict with any of the API routes defined in the app. Nested folders are looked up in the static root folder.\nContent based hashing is not supported for static root files. These files are expected to be used for well known files like favicon.ico. Most regular static file serving use cases should use the static folder, not the static_root folder.","structured-template-layout#Structured Template Layout":"The default in Clace is to load all the templates in one parse operation. This is easy to get started with but can result in challenges when the same template block needs to be duplicated in different files. Clace also supports a structured template layout. See this blog for an explanation about the differences between the two layouts. The default in Clace is the WordPress layout, all template files are loaded in one go. To use the second, Django layout, use the structured format.\nIf there is a base_templates folder in the app main folder with one or more *.go.html files, the structured template layout is used. In the structured layout format, all the base template files are loaded in one parse operation. Each of the files in the app main folder is then individually loaded. Each top level file has access to its own template blocks plus the base templates.\nThis has the advantage that the main templates can have duplicate templates, with no conflicts because they are loaded individually. For example, if there is a base_templates/base.go.html file with\n\u003chtml\u003e \u003chead\u003e\u003c/head\u003e {{block \"body\" .}} {{end}} \u003cfooter\u003e\u003c/footer\u003e \u003c/html\u003e {{end}} and a index.go.html file with\n{{define \"body\"}} My Index Body {{end}} {{- template \"full\" . -}} and a help.go.html file with\n{{define \"body\"}} My Help Body {{end}} {{- template \"full\" . -}} then a route using index.go.html will get the HTML for the index page and route using help.go.html with get HTML help page. Although the body is defined in two template files, there is no conflict since the root level template files are loaded independently.\nWithout structured template layout, if a duplicate block is found, the one to be used depends on the order in which the files are loaded. To change the folders used for base templates, set:\nsettings={ \"routing\": {\"base_templates\": [\"base_templates\", \"template_helpers\"]} } ","template-file-location#Template File Location":"Templates are loaded once on app initialization. In dev mode, they are automatically reload on file updates. By default, the app source home directory is searched for template files. This can be changed by adding this directive in the ace.app config.\nsettings={ \"routing\": {\"template_locations\": [\"*.go.html\", \"templates/*.go.html\"]} } the default is [\"*.go.html\"]. If additional directories are added, \"*.go.html\" still needs to present in the list since generated files are created in the app home directory. Also, all folders in the list need to contains at least one template file. File names have to be unique across folders. Files are referenced by their name, without the folder name, when used in template import directives."},"title":"Templates"},"/docs/installation/":{"data":{"":"","initial-configuration#Initial Configuration":"To use the clace service, you need an initial config file with the service password and a work directory. Create the clace.toml file, and create a randomly generate password for the admin user account\nclace password \u003e $CL_HOME/clace.toml This will print a random password on the screen, note that down as the password to use for accessing the applications.","install-from-source#Install from Source":"To install from source\nEnsure that a recent version of Go is available, version 1.21.0 or newer. Checkout the Clace repo. The below instructions assume you are using $HOME/clhome/clace.toml as the config file and $HOME/clhome as the work directory location. First add the below env variables to your shell .profile or .bash_profile:\nexport CL_HOME=$HOME/clhome export PATH=$CL_HOME/bin/:$PATH Source the update profile file, like source ~/.bash_profile. Build the Clace binary\n# Ensure go is in the $PATH mkdir -p $CL_HOME/bin mkdir $HOME/clace_source \u0026\u0026 cd $HOME/clace_source git clone -b main https://github.com/claceio/clace \u0026\u0026 cd clace go build -o $CL_HOME/bin/clace ./cmd/clace/ ","install-release-build#Install Release Build":"To install the latest release build on Linux, OSX or Windows with WSL, run the install script. Note down the password printed. Add the env variables as prompted and then start the service.\ncurl -L https://clace.io/install.sh | sh source $HOME/clhome/bin/clace.env clace server start \u0026\u0026 sleep 2 clace app create --approve github.com/claceio/clace/apps/system/disk_usage /disk_usage Clace is installed under $HOME/clhome by default. The disk usage app should be available at https://127.0.0.1:25223/disk_usage after allowing the self-signed certificate. admin is the username and use the password printed by the install script.\nOn Windows, to install the Clace application, run\npwsh -Command \"iwr https://clace.io/install.ps1 -useb | iex\" Use powershell if pwsh in not available. The app is installed under $HOME\\clhome by default. Note down the generated password for the admin user. Open a new command window (to get the updated ENV values) and run\nclace server start to start the server. In another window, apps can be installed using same command as Linux/OSX. To install a bookmark manager app, run\nclace app create --approve github.com/claceio/apps/utils/bookmarks /book The bookmark manager app should be available at https://127.0.0.1:25223/book.\nSee start the service for details.","load-an-app#Load an App":"To create an app, ensure that code is available locally and then run the Clace client\nclace app create --dev $HOME/clace_source/clace/examples/disk_usage /disk_usage To audit and approve the app’s security policies, run\nclace app approve /disk_usage This will create an app at /disk_usage with the example disk_usage app. The disk_usage app provides a web interface for the du command, allowing the user to explore the subfolders which are consuming most disk space.\nTo access the app, go to https://127.0.0.1:25223/disk_usage. Use admin as the username and use the password previously generated. Allow the browser to connect to the self-signed certificate page. Or connect to http://127.0.0.1:25222/disk_usage to avoid the certificate related warning.\nThe code for the disk usage app is in GitHub. app.star is the starlark config and app.go.html is the html template. The other files are generated files and are created by Clace during app development.","start-the-service#Start the service":"To start the Clace server, run\nclace server start The service logs will be going to $CL_HOME/logs. The service will be started on https://localhost:25223 by default."},"title":"Installation"},"/docs/plugins/":{"data":{"":"Overview of Clace plugins and how to use them\nOverviewOverview about Clace plugins, automatic error handling and accounts CatalogList of plugin and their APIs: exec, fs, http Store PluginStore plugin for persisting data to a database Container PluginContainer plugin supports configuring container backend Proxy PluginProxy plugin supports proxying API calls "},"title":"Plugins"},"/docs/plugins/catalog/":{"data":{"":"The page lists the available plugins and their API details.","container-config#Container Config":"The container.in plugin supports configuring for using a container for deploying backend APIs. See container for details.","database-storage#Database Storage":"The store.in plugin supports a document store interface for writing data to SQLite. See store for details.","exec-plugin#Exec Plugin":"run The exec.in plugin allows running external commands, starting a new process for the specified command. The APIs available are:\nAPI Type Notes run Read/Write Runs the command as a new process The API supports the following parameters:\npath (string, required) : the command to run args (list of strings, optional) : arguments to pass to the cmd env (list of strings, optional) : the env to pass to the cmd, in the form key=value process_partial (bool, optional) : whether to process the output when there is a failure stdout_file (bool, optional) : whether to send the stdout for the process to a temporary file on disk The default response (when stdout_file is False) for the exec API (value within plugin_response) is of type list of strings. The stdout is scanned and split on newlines. The list of lines is returned. For example\napp.star ret = exec.run(\"ls\", [\"-l\", \"/\"], process_partial=True) if ret.error: return {\"Error\": ret.error} for line in ret.value: # Process lines ⚠️ Note: Only first 100MB of the command stdout output is scanned currently, the rest is discarded. If stdout_file is True, then the plugin output value is the name for the temp file to which the output was written. There is no size limit on this file output. The user is responsible for deleting this file.","fs-plugin#FS Plugin":"The fs.in allows working with local file system. The APIs available are\nAPI Type Notes abs Read Returns the absolute path for given relative path list Read List files in specified directory find Read Find files under specified directory matching criteria serve_tmp_file Read Load file metadata to the Clace database and make available through API abs The abs API supports the following parameter:\npath (string, required) : the file path The response for the API (value within plugin_response) is of type string, the absolute path for given path.\nlist The list API supports the following parameters:\npath (string, required) : the directory path recursive_size (bool, optional, default false) : whether to include the recursive size of sub-directories ignore_errors (bool, optional, default false) : whether to ignore errors when accessing entries The response for the API is a list of type FileInfo. The FileInfo struct contains the fields:\nname (string) : the file name size (int) : the file size in bytes, rounded up to 4K is_dir (bool) : is it a directory mode (int) : file mode info find The find API supports the following parameters:\npath (string, required) : the directory path name (string, optional) : the file name glob pattern to match limit (int, optional, default 10K, max 100K) : the limit on number of entries to return min_size (int, optional) : the minimum file size in bytes to look for ignore_errors (bool, optional, default false) : whether to ignore errors when accessing entries The response for the find API is a list of type FileInfo, same as returned by list.\nserve_tmp_file The serve_tmp_file API supports the following parameters:\npath (string, required) : the file path to serve name (string, optional, default basename of path) : the file name to use (when serving the API) visibility (string, optional, default fs.USER) : the API access level for the file (fs.USER or fs.APP) mime_type (string, optional, default application/octet-stream) : the mime type to use when serving the file expiry_minutes (int, optional, default 60 minutes) : how long to keep the file metadata, set to zero for no deletion single_access (bool, optional, default True) : whether to serve the file just once and then automatically delete it The response for the serve_tmp_file API is a dict with the fields:\nid (string) : the id of the metadata entry url (string) : the url path (without domain) for downloading the file name (string) : the file name The serve_tmp_file API creates a metadata entry in the Clace database. The file can be served through an API using this entry. The default behavior is the file is accessible only to the user who created it. First download of the file will serve the file and automatically deletes the file. The file is deleted after 60 minutes in case there is no API access before that. Deleting the metadata entry removes the database entry and also deletes the file from disk. If the file should not be deleted, do:\nret = fs.serve_tmp_file(\"/tmp/myfile\", single_access=False, expiry_minutes=0)\nSee number_lines app code:demo for an example of using this API. Setting visibility to fs.APP will make the API available to anyone who has access to the app.","http-plugin#HTTP Plugin":"get,head,options,post,put,delete,patch The http.in plugin supports making HTTP API calls. The APIs available are:\nAPI Type Notes get Read HTTP Get request head Read HTTP Head request options Read HTTP Options request post Write HTTP Post request put Write HTTP Put request delete Write HTTP Delete request patch Write HTTP Patch request All the API’s support the following parameters:\nurl (string, required) : the url to send the request to params (dict, optional) : url params to send headers (dict, optional) : HTTP headers to send body (string, optional) : body to send form_body (dict, optional) : form body to send form_encoding (string, optional) : the form encoding to use, application/x-www-form-urlencoded (default) or multipart/form-data json_body (object, optional) : the object to send as json encoded body auth_basic (tuple(string, string), optional): HTTP basic auth username and password The response for all API’s (value within plugin_response) contains following properties:\nurl (string): the url the response is for status_code (int): the HTTP status code headers (dict): the output headers encoding (string): the transfer encoding header body() (string) : the response body as a string json() (object) : the response body un-marshalled as a json If the API calls fails to go through then the plugin response error property will be set. If the API goes through, then the response error will not be set, even if API call fails with an HTTP error. The status_code will indicate whether the API succeeded on the server. To handle all possible error conditions, do (change to handle all 2xx codes if required)\napp.starret = http.get(\"http://localhost:9999/test\") if ret.error or ret.value.status_code != 200: return # error handling val = ret.value.json() # success handling ","proxy-config#Proxy Config":"The proxy.in plugin supports proxying API calls to deploying backend APIs. See proxy for details."},"title":"Plugin Catalog"},"/docs/plugins/container/":{"data":{"":"The container.in plugin provides the config API to allow configuring the container backend.","api#API":"The container.in plugin has just one api, config\nAPI Type Notes config Read Configures the container details for the app The config API supports the following parameter:\nsrc (string, optional) : the source for containerfile, auto by default port (int, optional) : the port number exposed from the container scheme (string, optional) : the url scheme, http by default health (string, optional) : the health check API, / by default lifetime (string, optional) : the lifetime for the container, currently unused build_dir (string, optional) : the build directory for the build, / by default When the src is auto, the container file is auto detected. It checks for presence of either Containerfile or Dockerfile. If the value begins with image:, the subsequent portion is treated as the image to download. No image build is done in that case. Any other value for src is treated as the file name to use as the container file.\nport can be specified in the container file, using a EXPOSE directive. If a value other than zero is specified in the config, that takes precedence over the value in the Expose.\nA sample program using the container config is\napp.starload(\"proxy.in\", \"proxy\") load(\"container.in\", \"container\") app = ace.app(\"My App\", routes=[ ace.proxy(\"/\", proxy.config(container.URL)) ], container=container.config(container.AUTO), permissions=[ ace.permission(\"proxy.in\", \"config\", [container.URL]), ace.permission(\"container.in\", \"config\", [container.AUTO]) ] ) ","introduction#Introduction":"Clace can build and manage containers for implementing the app backend APIs. The config API is used to configure at the app level what configuration is used for the container."},"title":"Container Plugin"},"/docs/plugins/overview/":{"data":{"":"Plugins provide an API for Clace Starlark code to call out to external systems. Plugins are implemented in Go. Every plugin API calls needs to be in the approved list for it to be permitted. See security for an overview of the security model.\nEach plugin is identified by a unique name, like store.in or exec.in. Plugins ending with .in are internal plugins, built into the Clace binary. Support for external plugins which are loaded dynamically is planned.","automatic-error-handling#Automatic Error Handling":"Clace supports automatic error handling, so that the handler functions do not have to check the error status of every plugin API call. The way this is implemented is such that if no explicit error handling is done, then the automatic error handling kicks in. If explicit error handling is done, then automatic error handling is not done. See bookmarks app for an example of how the automatic error handling can be used.\nIf the error_handler function is defined, then that is called with the error. The manual error checking works the same as mentioned above. But if no manual error checking is done, then the Clace platform will automatically call the error_handler function in case of an error. The error_handler could be defined as:\napp.stardef error_handler(req, ret): if req.IsPartial: return ace.response(ret, \"error\", retarget=\"#error_div\", reswap=\"innerHTML\") else: return ace.response(ret, \"error.go.html\") When no explicit error checks are done, the automatic error handling happens in these three cases:\nValue Access When the response value is accessed Next API call When the next plugin API call happens (to any plugin function) Handler Return When the handler function returns Value Access If the handler code is\napp.star ret = http.get(\"https://localhost:9999/test\") print(ret.value.json()) If the get API had succeeded, then the value property access will work as usual. But if the get API had failed, then the value access will fail and the error_handler will be called with the original request and the error response.\nNext API Call If the value is not being accessed, then the next plugin call will raise the error. For example, if the handler code is\napp.star store.begin() bookmark = store.select_one(table.bookmark, {\"url\": url}).value The response of the begin API is not checked. When the next select_one API is called, if the previous begin had failed, the select_one API will raise the previous API call’s error, the select_one will not run.\nHandler Return If the handler code is\napp.star def insert(req): store.begin() book = doc.bookmark(\"abc\", []) store.insert(table.bookmark, book) store.commit() Assume all the API calls had succeeded and then the commit fails. Since the value is not accessed and there is no plugin API call after the commit call, the Clace platform will raise the error after the handler completes since the commit had failed.\nOverriding Automatic Error Handling The automatic error handling is great for handling the unusual error scenarios. For the error scenarios which are common, like a database uniqueness check failure, the error handing can be done explicitly in the handler code. If the handler code is\napp.starret = store.insert(table.bookmark, new_bookmark) if ret.error: return ace.response(ret, \"error.go.html\") The automatic error handling will not be invoked in this case since the ret.error is being checked. Checking the truth status of ret also will disable the automatic error handling:\napp.starret = store.insert(table.bookmark, new_bookmark) if not error: return ace.response(ret, \"error.go.html\") ℹ️ Note: When developing a new app, first define the error_handler and test it for the partial and full page scenarios. All subsequent handler code does not need to handle errors unless specific handling is required. If no error_handler is defined, a generic error message screen is returned. If is recommended to define a custom error_handler. ","plugin-accounts#Plugin Accounts":"Some plugins like exec.in do not require any account information. Others like store.in need some account information. The account configuration for a plugin is loaded from the Clace config file clace.toml. For example, the default configuration for store.in is here, which contains:\nclace.toml[plugin.\"store.in\"] db_connection = \"sqlite:$CL_HOME/clace_app.db\" Any application using the store.in plugin will by default use the $CL_HOME/clace_app.db sqlite database. To change the default account config used by apps, update clace.toml and restart the Clace server. For example, adding the below will overwrite the default store.in config for all apps.\nclace.toml[plugin.\"store.in\"] db_connection = \"sqlite:/tmp/clace_app.db\" Account Linking If specific account config is required for an app, then the app can be linked to a specific account config. First add a new account config by adding in clace.toml\nclace.toml[plugin.\"store.in#tmpaccount\"] db_connection = \"sqlite:/tmp/clace_app.db\" For an app /myapp using store.in, run clace account link --promote /myapp store.in tmpaccount\nThis links the myapp app to use the tmpaccount account.\nNamed Account In addition to using account linking, the plugin code itself can point to specific accounts. For example, if the app code has\napp.starload(\"http.in#google\", \"googlehttp\") then the app will use the http.in#google account config by default. This also can be overridden using account links, by running clace account link --promote /myapp http.in#google myaccount\nThis approach is useful if an app has to access multiple accounts for the same plugin. The account linking approach is recommended for normal scenarios.\nClace apps aim to be portable across installations, without requiring code changes. Using account config allows the app code to be independent of the installation specific account config.","plugin-usage#Plugin Usage":"To use a plugin, load it using\napp.starload(\"http.in\", \"http\") This adds http to the namespace for the app. To make a call to the plugin, first add the permissions to the app config.\napp.star permissions=[ ace.permission(\"http.in\", \"get\"), ace.permission(\"http.in\", \"post\") ], Run clace app approve /myapp to authorize the app to call the get and post methods on the http plugin.\nIn the app handler code, do\napp.star ret = http.get(SERVICE_URL + \"/api/challenge/\" + challenge_id) if not ret: return ace.response(ret.error, \"invalid_challenge_id\", code=404) At runtime, Clace will check if the get call is authorized. If so, the call to the plugin will be performed.","response-handling#Response Handling":"All plugin API calls return a plugin_response structure. The fields in this are\nerror The error message string, empty string if no error error_code The error code integer, zero if no error value The actual return value for the plugin API call. The datatype for this depends on the API, check the API documentation for details. To check the error status of an API call:\nCheck boolean value for the return. If false, that indicates an error which can be returned. If no error, get the value property and continue with processing For example,\napp.star ret = http.get(\"https://localhost:9999/test\") if not ret: # error condition return ace.response(ret, \"error_block\") # success print(ret.value.json()) # ret.value is the return value. The http plugin response has a json() function An alternate way to write the error check is\napp.star ret = http.get(\"https://localhost:9999/test\") if ret.error: # error condition return ace.response(ret, \"error_block\") # Success print(ret.value.json()) "},"title":"Plugin Overview"},"/docs/plugins/proxy/":{"data":{"":"The proxy.in plugin provides the config API to allow proxying of API calls.","api#API":"The proxy.in plugin has just one api, config\nAPI Type Notes config Read Configures the proxy details for the route The config API supports the following parameter:\nurl (string, required) : The url to proxy to. Use container.URL to proxy to backend container strip_path (string, optional) : extra path values to strip from the proxied API call preserve_host (bool, optional) : whether to preserve the Host header. Default false, the Host header is set to the target host value strip_app (bool, optional) : whether to strip the app path from the proxied API call. Default true. ","example#Example":"This is an example app which proxies data to google.com. This app has to be installed at the root level, since google does not use relative paths.\napp.starload(\"proxy.in\", \"proxy\") app = ace.app(\"Proxy App\", routes=[ ace.proxy(\"/\", proxy.config(\"https://www.google.com\")) ], permissions=[ ace.permission(\"proxy.in\", \"config\", [param.url]), ] ) ","introduction#Introduction":"Clace can proxy API calls to external endpoints or to backend APIs implemented in a container. The config API is used to configure at the route level what configuration is used for the proxy."},"title":"Proxy Plugin"},"/docs/plugins/store/":{"data":{"":"The store.in plugin provides a document store interface to work with SQLite tables (PostgreSQL support is coming soon). The goal for the store plugin is to support a full managed interface, creating the app automatically creates the tables required for the app.","automatic-fields#Automatic Fields":"All tables have some fields added automatically. These are:\nField Type Notes _id int Primary key _version int Schema version _created_by string User id _updated_by string User id _created_at timestamp _updated_at timestamp These fields can be accessed like regular user defined field in the store APIs. So bookmark._id can be used the way bookmark.url is used in all the APIs.","filter#Filter":"The select, select_one, count and delete APIs take a filter parameter. The filter has to be specified as a dict. The format of the filter is similar to the format used by MongoDB. The advantage of this over a SQL expression is that there is no possibility of SQL injection, even with an improperly written application.\nThe filter is specified as a list diction, the keys are the names of the field to apply the condition on. The value can be a value, in which case it is treated as a equality match. If the value is an diction, then the it is treated as a expression to apply on the specified field.\nFor example, a filter {\"age\": 30} is equivalent to sql where clause age = ? with the parameter bound to 30. Filter {\"age\": 30, \"city\": \"New York\", \"state\": \"California\"} is same as sql age = ? AND city = ? AND state = ?, with the appropriate bindings. To express an or condition, do filter as {\"age\": 30, \"$or\": [{\"city\": \"New York\"}, {\"state\": \"California\"}]}. That translates to age = ? AND ( city = ? OR state = ? )\nTo express an inequality condition, do {\"age\": {\"$gt\": 30}} which becomes age \u003e ?.\nThe logical operators supported are $AND and $OR, case insensitive.\nThe filter operators supported (case insensitive) are\nFilter SQL Notes $GT \u003e $LT \u003c $GTE \u003e= $LTE \u003c= $EQ = Default when value is not a dict $NE != $LIKE like Value has to be passed with % added, it is not added automatically. For example \"%test%\" ","introduction#Introduction":"The store.in plugins automatically creates tables with the specified schema. The tables are created on first load unless they are already present. The tables are linked to the app. The tables use a document store interface. The data is stored as JSON(B) data types. To query the data, a structured interface is used similar to the one provided by MongoDB. The advantage of this approach is that SQL injection is not possible, even if the application code is incorrectly written.","iterators#Iterators":"The select API returns a document iterator. Use a regular python for loop to iterate on the entries. For example,\napp.starret = store.select(table.bookmark, {}, limit=100, sort=[\"_created_at:desc\"]) if ret.error: return ace.response({\"error\": ret.error}, \"error\") bookmarks = [] for row in ret.value: bookmarks.append(row) Iterating till the end of the loop automatically closes the iterator. Returning from a handler without closing an iterator will cause the handler to fail. The iterator is automatically closed by the Clace platform to prevent a resource leak. The API failure is used to indicate to the developer that the code needs to be fixed to explicitly close the iterator.\n⚠️ Note: The iterator cannot be directly returned from the handler. A list needs to be created and populated if the entries need to be passed to the template. ","schema-definition#Schema Definition":"The schema for the app is specified in the schema.star file in the root directory of the app code. The format of this file is like:\napp.startype(\"bookmark\", fields=[ field(\"url\", STRING), field(\"tags\", LIST), ], indexes=[ index([\"url\"], unique=True) ]) type(\"tag\", fields=[ field(\"tag\", STRING), field(\"urls\", LIST), ], indexes=[ index([\"tag\"], unique=True) ]) Multiple types can be specified. Each type has a name, list of fields and list of indexes. Each field has a name and a type, the valid types are INT, STRING, BOOLEAN, LIST and DICT.\nEach type maps to one table in the underlying database. Indexes can be created on the fields. Each index is specified as list of field names. Adding :desc to the field name changes the index to be sorted descending instead of default ascending. Setting unique property to True makes it an unique index.","schema-design#Schema Design":"SQL tables are used as underlying storage, but joins are not supported by the store.in interface. The schema design to use would be same as schema used for a document database. Since LIST and DICT data types are supported, de-normalized schema is recommended instead of normalized schema.","select-limits-and-sort#Select Limits and Sort":"For the select API, a limit of 10,000 is set as the default limit value. The API can pass a different limit value if required. The maximum value allowed for the limit is 100,000. Passing a limit beyond that will result in an API failure.\nThe sort argument can be used to sort the result for the select API. The argument is a list of strings. For example, [\"age\", \"city\"] is sorted on age and city ascending. [\"age\", \"city:desc\"] is sorted on age ascending and city descending.","store-apis#Store APIs":"The API’s in the store.in plugin and their arguments and response are:\nAPI Type Args Response Value Notes begin Read - - Begin a new transaction commit Write - - Commit active transaction rollback Read - - Rollback active transaction select_by_id Read table: string id : int doc Select one record by id select Read table: string filter : dict sort : list string offset : int limit : int (default 10,000) doc iterator Select by filter select_one Read table: string filter : dict doc Select one by filter count Read table: string filter : dict int Count entries by filter insert Write table: string entry : doc id : int Insert a document update Write table: string entry : doc count : int Update a document delete_by_id Write table: string id : int count : int Delete one document by id delete Write table: string filter : dict count : int Delete multiple docs by filter ","store-types#Store Types":"The type information is read from the schema file and schema types are automatically created for the app. The doc namespace has type objects for each type. For the previous example, the two types created are doc.bookmark and doc.tag. The table namespace also has entry populated which reference the table names for the type. For the previous example, the two table names available are table.bookmark and table.tag. This allows creating objects and persisting them using the store API by doing:\napp.starbookmark = doc.bookmark( url=\"http://clace.io\", tags=[\"webapps\", \"tools\"]) ret = store.insert(table.bookmark, bookmark) ","transactions#Transactions":"The transaction handling APIs begin, commit and rollback take no arguments. All the other APIs take the table name as the first argument. The transaction created by the begin is saved in a thread local, there is no need to pass the transaction manually to subsequent API calls. The transaction rollback is automatically done at the end of the API handler if commit is not done explicitly."},"title":"Store Plugin"},"/docs/quickstart/":{"data":{"":"Clace is an Apache-2.0 licensed project building a web app development and deployment platform for internal tools. This page provides an overview of how to start with Clace and provides links to doc pages with more details.","action-apps#Action Apps":"For use cases where an existing CLI application or API needs to be exposed as a web app, actions provide an easy solution. First, define the parameters to be exposed in the form UI. Create a params.star file with the params. For example,\nparams.starparam(\"dir\", description=\"The directory to list files from\", default=\"/tmp\") The app defines a run handler which runs ls on the specified directory. The output text is returned.\napp.starload (\"exec.in\", \"exec\") def run(dry_run, args): out = exec.run(\"ls\", [\"-Lla\"]) if out.error: return ace.result(out.error) return ace.result(\"File listing for \" + args.dir, out.value) app = ace.app(\"List Files\", actions=[ace.action(\"List Files\", \"/\", run, description=\"Show the ls -a output for specified directory\")], permissions=[ ace.permission(\"exec.in\", \"run\", [\"ls\"]), ], ) The app, when accessed will look as shown below, with the ls command output displayed:\nSee list files code:demo for the above app. See dictionary code:demo for another actions example app which shows different type of reports. Actions has more details on building app actions.","app-installation#App Installation":"To install apps, run clace app create --approve \u003csource_url\u003e \u003c[domain:]app_path\u003e. For example,\nclace app create --approve github.com/claceio/apps/system/disk_usage /disk_usage This is installing the system/disk_usage app from the main branch of the claceio/apps repo on GitHub. The app is installed for the default domain, to the /disk_usage path. Opening https://127.0.0.1:25223/disk_usage will initialize the app and show the app home page.\n⚠️ The /disk_usage/* path is now reserved for API’s under this app. No new apps can be installed under the /disk_usage/ path, but /disk_usage2 is available. Similarly, installing an app under / path means no new apps can be installed for the default domain. If the app code is available on the Clace server node, the app create can be done directly with the local disk path:\nclace app create --approve ./diskapp /disk_usage_local When developing an app, the source code for the app has to be present locally. To install an app in dev mode, add the --dev option.\nclace app create --dev --approve ./diskapp /disk_usage_dev In dev mode, source code changes are picked up immediately and the app is live reloaded. For non-dev (prod) apps, app reload has to be done to pick up changes, from local disk or from git.\nclace app reload --approve --promote \"/disk_usage*\" For apps created from GitHub source, app reload will pick up the latest changes from the branch specified during app create (default is main). For apps created from local disk sources, the reload loads from the folder originally used during the create. For non-dev apps, the source code is loaded into the SQLite metadata database managed by the Clace server.This allow for versioning, even when working with local sources.","app-listing#App Listing":"Use clace app list to get list of installed app. By default, all apps are listed. Use a glob pattern like example.com:** to list specific apps. Pass the --internal or -i option to list to include the internal apps in the app listing. The pattern matches the main apps, and if the internal option is specified, the matched app’s linked apps are also listed.\nUse clace version list to get list of versions for an app. clace version switch allows switching between versions. The version command can be run separately on the staging app and prod app, like clace version list /myapp_cl_stage and clace version list /myapp. The current version is indicated in the output.\n$ clace version list /dugit Active Version Previous CreateTime GitCommit GitMessage 1 0 2024-02-16 19:39:05 +0000 UTC 03ccaa35927667977646 Added version file listing support 2 1 2024-02-16 19:55:51 +0000 UTC 03ccaa35927667977646 Added version file listing support =====\u003e 3 2 2024-02-16 21:18:16 +0000 UTC c00d7b1e99712de13745 Added version switching support $ clace version list /dugit_cl_stage Active Version Previous CreateTime GitCommit GitMessage 1 0 2024-02-16 19:39:05 +0000 UTC 03ccaa35927667977646 Added version file listing support 2 1 2024-02-16 19:54:22 +0000 UTC 03ccaa35927667977646 Added version file listing support 3 2 2024-02-16 20:38:44 +0000 UTC c00d7b1e99712de13745 Added version switching support =====\u003e 4 3 2024-02-16 21:18:42 +0000 UTC c00d7b1e99712de13745 Added version switching support $ clace app list -i /dugit Id Type Version Auth GitInfo Domain:Path SourceUrl app_prd_2cSkPeHiATfH46pcUX8EdZqdWQb PROD* 3 SYST main:c00d7b1e99712de13745 /dugit github.com/claceio/clace/examples/disk_usage app_stg_2cSkPeHiATfH46pcUX8EdZqdWQb STG 4 SYST main:c00d7b1e99712de13745 /dugit_cl_stage github.com/claceio/clace/examples/disk_usage In the above listing, the staging app is on version 4, prod app on version 3. The * in the app list output indicates that the prod app has staged changes waiting to be promoted. Running clace app promote /dugit will update prod with the staged changes. version revert reverts to previous version. version switch can be used to switch to particular version, next and previous are shortcuts for version numbers. Version commands run against the specific app, so revert can be done on the staging app or the main app independently.","app-security#App Security":"Application config is specified in Starlark code in the app.star file. By default, the app does not have any permissions. All external actions an app can perform are done through plugin API calls. Every plugin API call needs to be approved before it is allowed. This allows for multiple apps to run on the Clace server without interfering with each other.\nTo approve an app permissions, run\nclace app approve /disk_usage The --approve option can be specified during the app create and app reload command to automatically approve the permissions.","application-types#Application Types":"Clace allows easy management of multiple apps on one Clace server installation. There are three main types of Clace apps:\nAction apps - App backend is defined in Starlark and an auto generated form UI and report is created by Clace. These are the simplest apps. Containerized Apps - App backend (in any language/framework) runs in a container. Clace acts as an application server doing reverse proxying for the app APIs. This allows Clace to install and manage apps built in frameworks like Streamlit/Gradio/FastHTML/FastAPI/Flask etc. Hypermedia apps - The app is completely customizable, allowing combining containerized apps with actions and custom API handlers, building Hypermedia driven UIs. For all apps, Clace provides blue-green staged deployment, OAuth access controls, secrets management, TLS cert management etc.","containerized-applications#Containerized Applications":"Clace apps which are implemented in Starlark run within the Clace server. No containers are required for running those apps. For apps where the backend is implemented in any other language, containers are used to run the app. Clace works with Docker and Podman. By default, the server looks for the podman client CLI. If not found, it looks for the docker client CLI. To customize this, add in server config\nclace.toml[system] container_command = \"/path/to/container_manager_cli\" There are two options for using containerized apps. One is to include the required files in the app repo. This will mean there should be app.star with the app config, a Containerfile or Dockerfile with the container config. The other option is to use an app spec. This allows you to use Clace without requiring any changes to your app. No container file is even required. For example, the command\nclace app create --spec python-streamlit --branch master --approve \\ github.com/streamlit/streamlit-example /streamlit does the following:\nChecks out the github.com/streamlit/streamlit-example Copy any missing files from the app specification python-streamlit into the repo Load the app source and metadata into the Clace server metadata database (SQLite) When the first API call is done to the app (lazy-loading), the Clace server will build the container image from the Containerfile defined in the spec, start the container and setup the proxy for the app APIs.\nAny env params which need to be passed to the app can be configured as app params. Params are set, during app creation using app create --param port=9000 or after creation using param update port 9000 /myapp.\nIf the source repo has a Containerfile or Dockerfile, the container spec is a generic spec which works with any language or framework. If the container file defined a port using EXPOSE directive, then port is not required. Otherwise, specify a port, for example\nclace app create --spec container --approve --param port=8000 \\ github.com/myorg/myrepo /myapp See containerized apps for details.","developing-apps#Developing Apps":"Clace app backend can be written in any language, running in a container. Some apps can be written in Starlark and Go HTML templates, in which case no containers are required.\nSee dev overview for a quick start overview on developing Clace applications.","installation#Installation":"Install On OSX/Linux To install on OSX/Linux, run\ncurl -L https://clace.io/install.sh | sh source $HOME/clhome/bin/clace.env clace server start \u0026 Install On Windows To install on Windows, run\npwsh -Command \"iwr https://clace.io/install.ps1 -useb | iex\" Use powershell if pwsh is not available. Start a new command window (to get the updated env) and run clace server start to start the Clace service.\nInstall Apps Once Clace server is running, to install apps, run:\nclace app create --approve github.com/claceio/apps/system/list_files /files clace app create --approve github.com/claceio/apps/system/disk_usage /disk_usage clace app create --approve github.com/claceio/apps/utils/bookmarks /book The disk usage app is available at https://localhost:25223/disk_usage (port 25222 for HTTP). admin is the username, use the password printed by the install script. The bookmark manager is available at https://localhost:25223/book, the list files app is available at https://localhost:25223/files. Add the --auth none flag to the app create command to disable authentication.\nSee installation for details. See config options for configuration options. To enable Let’s Encrypt certificates, see Automatic SSL.\nThe release binaries are also available at releases. See install from source to build from source.","lifecycle-with-git#Lifecycle With Git":"If using git, a workflow would be:\nCreate a dev mode app, like clace app create --dev --approve ~/myappcode /myapp_dev Create a prod mode app, like clace app create --approve github.com/myorg/repo /myapp As code changes are saved to disk, the changes are immediately live at https://localhost:25223/myapp_dev When code is in a stable state, check in the dev code to git. Run clace app reload /myapp. This will update the staging app with the most recent code from main branch in git. The staging app is live at https://localhost:25223/myapp_cl_stage. Verify the functionality of the staging app. To promote the code to prod, run clace app promote /myapp. The staged code is promoted to prod, live at https://localhost:25223/myapp. ","lifecycle-without-git#Lifecycle without Git":"If not using git, a workflow would be:\nCreate a dev mode app, like clace app create --dev --approve ~/myappcode /myapp_dev Create a prod mode app, like clace app create --approve ~/myappcode /myapp As code changes are saved to disk, the changes are immediately live at https://localhost:25223/myapp_dev When code is in a stable state, run clace app reload /myapp. This will update the staging app with the most recent code from ~/myappcode folder. The staging app is available at https://localhost:25223/myapp_cl_stage for verification. To promote the code to prod, run clace app promote /myapp. The staged code is promoted to prod, live at https://localhost:25223/myapp. Having a staging environment helps catch issues related to account setup (which endpoint is pointed to etc) and other config issues before the changes are live on prod. Clace implements versioning for prod apps, even when source is not from git.","managing-applications#Managing Applications":"Multiple applications can be installed on a Clace server. Each app has a unique path and can be managed separately. The app path is made up of domain_name:url_path. If no domain_name is specified during app creation, the app is created in the default domain. The default domain is looked up when no specific domain match is found. See app routing for details about routing.\nFor local env, url based routing can be used or *.localhost domain can be used for domain based paths. For production deployment, if wildcard DNS is setup, domain based routing can be used without new DNS entries being required per app. Apps can be hosted on multiple unrelated domains on one Clace server.","staged-deployments#Staged Deployments":"For dev mode apps, there is just one app. For a prod mode app, creating the app creates a staging app and the actual production app. All config and code changes are applied on the staging mode app first, and then manually promoted using app promote. Promotion is automatic if --promote option is specified for the app reload (or any other command performing a metadata change).\nThe app list command lists all the apps for the specified glob pattern. By default, it lists only the dev and prod apps. To list the staging apps also, add the --internal (or -i) option to app list. all is a shortcut for *:**, which means all apps in all domains. all is the default for app list. For example:\nclace app list --internal all lists all the apps and internal apps for each app. clace app list \"example.com:**\" lists the main apps for the example.com domain.\nThe staging app can be used to verify whether changes are working before the production app is updated. The staging app is accessible by suffixing _cl_stage at the end of the prod app path. So for an app at https://example.com/, the staging url is https://example.com/_cl_stage. For an app at /utils/app1, the staging app url is /utils/app1_cl_stage.\nTo promote changes from staging to prod, run:\nclace app promote all or clace app promote \"/disk_usage*\" to promote specific apps. Use the --dry-run option to verify commands before they are actually applied."},"title":"Quick Start"},"/features/":{"data":{"":" GitOps Workflow Blue-green (staged) deployments, versioning and preview environments with no infra to manage.\nHypermedia web apps Fast and lightweight backend driven apps, minimal frontend complexity.\nSecrets Management Manage secrets with AWS Secrets Manager and Vault.\nAuto-Pause Idle apps Idle pause are paused, scale down to zero.\nAutogen Actions Auto generated UI for backend actions, no UI to develop.\nAudit Events Auto-audit logging for all events, plus custom events.\nCross-language AppServer Application Server which supports all languages.\nContainer management Automatically build and and deploy containers, with Docker or Podman.\nCross-platform support Clace runs on Linux, Windows and OSX, works with Docker and Podman\nAuto TLS Certificates Automatically generate TLS certificates, for multiple domains\nOAuth authentication Add OAuth2 or OIDC based authentication to any app\nDomain based and path based routing Install apps at a domain, subdomain or at path level\nPreview Apps Create preview apps from CI, allowing for changes to be reviewed before merge\nBlue-green Deployment Staged deployment, for code changes and for config changes\nSecurity Sandbox Apps using Starlark based micro-framework use sandboxing for security"},"title":"Features"}} \ No newline at end of file