# Spin
An Embeddable WebAssembly container runtime.

# Why?
Containers are lightweight, portable, and have minimal CPU overhead. However, they require a runtime (e.g., Docker), lack robust isolation, and are not universally portable across different OSes or CPU architectures.

Running containers in a secure and restricted environment is crucial when the code cannot be trusted, such as when it's generated by third parties or AI.

It's also beneficial to have a fallback in case the host operating system lacks a runtime.

Additionally, this enables the embedding of commands and services written in various languages into your code.

# Architecture
`spin` consists of a wasm runtime, an emulator for amd64/riscv64 compiled to wasm, and a minimal OS image that launches your container inside the emulator.

# Usage
## Registry
A container image registry.
```go
import "github.com/taubyte/tau/pkg/spin/registry"

r, err := New(context.TODO(), "path/to/storage")
```

Then, pull:
```go
err = r.Pull(context.TODO(), imageName, nil)
```

To get image path:
```go
imagePath, err := r.Path(imageName)
```

## Runtime
Execute containers sharing the same runtime or base module.

### Using Registry
```go
import (
    "github.com/taubyte/tau/pkg/spin/registry"
    "github.com/taubyte/tau/pkg/spin/runtime"
)

r, _ := New(context.TODO(), "path/to/storage")
s, _ := runtime.New(context.TODO(), runtime.Runtime[runtime.RISCV64](r))

r.Pull(context.TODO(), "riscv64/hello-world:latest", nil)

c, _ := s.New(runtime.Image("riscv64/hello-world:latest"))

c.Run()
```

Output:
```
Hello from Docker!
```

### Not using Registry
```go
import "github.com/taubyte/tau/pkg/spin/runtime"

s, _ := runtime.New(context.TODO(), runtime.Runtime[runtime.RISCV64](r))

c, _ := s.New(runtime.ImageFile("path/to/file"))

c.Run()
```

A good example is `TestPullContainer`.

### Executing Commands
If you'd like to execute a command, use the `Command` options. Example:
```go
c, err := s.New(Command("ls","/import"))
```

### Export Environment variables
Exporting Environment Variables is done using the `Env` options. Example:
```go
c, err := s.New(Env("SPIN","is awesome"),Command("sh","-c","\"echo \$SPIN\""))
```


### Mount a folder
To mount a folder to the container, use the `Mount` options. Example:
```go
c, err := s.New(Mount("/path/on/host","/path/in/container"),Command("ls","/path/in/container"))
```

Note: WASI does not export permissions.


### Networking
To enable networking just use the `Networking()` option when creating the container. Options like `Forward` can be used for port forwarding.

Example:
```go
c, err := s.New(Networking(Forward("18080", "8080")))
```

See `TestNetServiceContainer` and `TestNetContainer` for more details.



# Limitations
## Performance
Currently, WebAssembly is single-threaded, so only one CPU can be emulated. Generally, expect execution speeds to be about 10 times slower than native code on a single core.

## Pull speed
Right now, image compression may take a few minutes as it uses `mksquashfs`, which is CPU intensive, running on `spin`. Later we'll create two images, one using `mkisofs`, which will be used temporarily, and another one with `mksquashfs` in the background.

## Memory
WebAssembly modules cannot address more than 4GB and [wizer](https://github.com/bytecodealliance/wizer) is having a hard time optimizing the module when the memory exceeds 1GB. Thus, the runtime is presently limited to 1GB. Plans include using swap files that are in-memory on the host side to increase container memory in the future.