Changelog background image

Never miss a release, subscribe to our product updates.

We protect your privacy.

Thank you!

You will now be informed about our future product updates.

Star Passbolt on Github

It’s the best way to receive release notes instantly and show that you support Passbolt.

Star Passbolt repo
Github illustration
All articles

Running Passbolt on a Nintendo 3DS

18 min. read

Passbolt team

Passbolt team

5 November, 2026

Running Passbolt on a Nintendo 3DS

How to read this post

$ go tool dist list | grep arm$
android/arm
freebsd/arm
linux/arm
netbsd/arm
openbsd/arm
plan9/arm
func (s *Session) Login(privateKey string, passphrase []byte) (LoginStats, error)
func (s *Session) List() ([]Entry, error)
func (s *Session) Get(id string) (*Secret, error)
func (s *Session) Logout() error
GOOS=wasip1 GOARCH=wasm go build -tags getaddrinfo 
-buildmode=c-shared -trimpath -ldflags="-s -w" ./wasm
//go:wasmexport pb_login
func pbLogin(keyPtr unsafe.Pointer, keyLen uint32, passPtr unsafe.Pointer, passLen uint32) int32 {
	return call(func(s *core.Session) (any, error) {
		return s.Login(string(mem(keyPtr, keyLen)), mem(passPtr, passLen))
	})
}
var result []byte
func call(f func(*core.Session) (any, error)) int32 {
	if session == nil {
		return fail(core.ErrNotLoggedIn)
	}
	v, err := f(session)
	if err != nil {
		return fail(err)
	}
	var b []byte
	if v != nil {
		if b, err = json.Marshal(v); err != nil {
			return fail(err)
		}
	}
	setResult(b)
	return statusOK
}
func setResult(b []byte) {
	clear(result) // may contain a password
	result = b
}
void pb_open(const char* const* env);
int pb_init(const char* config_json, pb_buf* out);
int pb_login(const char* armored_key, size_t key_len, const char* passphrase, size_t pass_len, pb_buf* out);
int pb_list(pb_buf* out);
int pb_get_secret(const char* id, pb_buf* out);
int pb_logout(pb_buf* out);
void pb_buf_free(pb_buf* b);
int pb_login(const char* armored_key, size_t key_len, const char* passphrase, size_t pass_len, pb_buf* out) {
	U32 k = put(armored_key, key_len);
	U32 p = put(passphrase, pass_len);
	U32 status = m8_PassboltExport8_pbX5Flogin(&g_inst, k, (U32)key_len, p, (U32)pass_len);
	drop(p);
	drop(k);
	return take(status, out);
}
U32 i22_wasiX5FsnapshotX5Fpreview110_randomX5Fget(wasmModuleInstance* inst, U32 buf, U32 len) {
	U8* p = mem_ptr(buf, len);
	if (!p) return W_FAULT;
	return plat_random(p, len) == 0 ? W_SUCCESS : W_IO;
}
uint64_t plat_realtime_ns(void);
uint64_t plat_monotonic_ns(void);
void plat_sleep_ns(uint64_t ns);
int plat_random(void* buf, size_t len);
void plat_console_write(int fd, const void* buf, size_t len);
void plat_fatal(const char* msg, int code);
#define EPOCH_1900_TO_1970_MS 2208988800000ULL
uint64_t plat_realtime_ns(void) {
	int64_t ms = (int64_t)(osGetTime() - EPOCH_1900_TO_1970_MS)
		- (int64_t)pb3ds_utc_offset_minutes * 60000;
	return (uint64_t)ms * 1000000ULL;
}
go
package main
// #include <stdlib.h>
import "C"
import (
	"context"
	"encoding/json"
	"net/http"
	"unsafe"
	"github.com/passbolt/go-passbolt/api"
	"github.com/passbolt/go-passbolt/helper"
)
func main() {}
// list logs in and returns the name of every password.
func list(url, key, passphrase string) ([]string, error) {
	ctx := context.Background()
	c, err := api.NewClient(http.DefaultClient, "", url, key, passphrase)
	if err != nil {
		return nil, err
	}
	if err := c.Login(ctx); err != nil {
		return nil, err
	}
	defer c.Logout(ctx)
	resources, err := c.GetResources(ctx, nil)
	if err != nil {
		return nil, err
	}
	names := []string{}
	for _, r := range resources {
		rType, err := c.GetResourceTypeCached(ctx, r.ResourceTypeID)
		if err != nil {
			return nil, err
		}
		_, name, _, _, _, _, err := helper.GetResourceFromDataWithOptions(c, r, api.Secret{}, *rType, false)
		if err != nil {
			return nil, err
		}
		names = append(names, name)
	}
	return names, nil
}
// pb_list returns a JSON string that C must free with pb_free.
//
//export pb_list
func pb_list(url, key, passphrase *C.char) *C.char {
	names, err := list(C.GoString(url), C.GoString(key), C.GoString(passphrase))
	var v any = names
	if err != nil {
		v = map[string]string{"error": err.Error()}
	}
	b, _ := json.Marshal(v)
	return C.CString(string(b))
}
//export pb_free
func pb_free(s *C.char) { C.free(unsafe.Pointer(s)) }
import ctypes, json, os
lib = ctypes.CDLL("./libpassbolt.dylib")
lib.pb_list.restype = ctypes.c_void_p  # keep the raw pointer to free it later
ptr = lib.pb_list(b"http://localhost:18080", open("alice.asc", "rb").read(),
                  os.environ["PB_PASSPHRASE"].encode())
names = json.loads(ctypes.string_at(ptr))
lib.pb_free(ctypes.c_void_p(ptr))
print(names)
static const U32 m8_PassboltDataSegments[100000][3] = {
	{132736U,0U,202826U},
	{335570U,202826U,1528U},
	/* ... */
};
for (k = 0; k < 100000U; k++) {
	LOAD_DATA((*i->m0),
		m8_PassboltDataSegments[k][0],
		m8_PassboltData + m8_PassboltDataSegments[k][1],
		m8_PassboltDataSegments[k][2]);
}
Transport: &http.Transport{
	DialContext: wasip1.DialContext,
	...
}
func Register() error {
	if err := selfTest(); err != nil {
		return err
	}
	crypto.RegisterHash(crypto.SHA256, New)
	return nil
}

Continue reading

Goodbye, DigitalOcean

2 min. read

Goodbye, DigitalOcean

Passbolt Community Edition is being deprecated on the DigitalOcean Marketplace from 1 December 2026. Learn what this means for existing instances and how to continue updating or migrate to a new server.

Antony Bartolomucci

Antony Bartolomucci

3 November, 2026

Making autofill improvements: Tackling the complexity of modern web

6 min. read

Making autofill improvements: Tackling the complexity of modern web

More accurate autofill, fewer false positives, and smoother sign-ins. Discover how Passbolt rebuilt its autofill detection algorithm in 5.17 to handle complex login forms.

Passbolt team

Passbolt team

29 October, 2026

Flag of European UnionMade in Europe. Privacy by default.