diff --git a/cmd/main.go b/cmd/main.go
index c40c337..81aad6f 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -13,13 +13,9 @@ import (
 	"git.defalsify.org/vise.git/lang"
 	"git.grassecon.net/grassrootseconomics/sarafu-vise/config"
 	"git.grassecon.net/grassrootseconomics/visedriver/storage"
-	httpremote "git.grassecon.net/grassrootseconomics/sarafu-api/remote/http"
-	devremote "git.grassecon.net/grassrootseconomics/sarafu-api/dev"
-	"git.grassecon.net/grassrootseconomics/sarafu-api/remote"
-	apievent "git.grassecon.net/grassrootseconomics/sarafu-api/event"
+	"git.grassecon.net/grassrootseconomics/sarafu-vise/services"
 	"git.grassecon.net/grassrootseconomics/sarafu-vise/args"
 	"git.grassecon.net/grassrootseconomics/sarafu-vise/handlers"
-	"git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/event"
 )
 
 var (
@@ -28,37 +24,9 @@ var (
 	menuSeparator = ": "
 )
 
-type devEmitter struct {
-	h *apievent.EventsHandler
-}
-
-func (d *devEmitter) emit(ctx context.Context, msg apievent.Msg) error {
-	var err error
-	if msg.Typ == apievent.EventTokenTransferTag {
-		tx, ok := msg.Item.(devremote.Tx)
-		if !ok {
-			return fmt.Errorf("not a valid tx")
-		}
-		logg.InfoCtxf(ctx, "tx emit", "tx", tx)
-		ev := tx.ToTransferEvent()
-		err = d.h.Handle(ctx, apievent.EventTokenTransferTag, &ev)
-	} else if msg.Typ == apievent.EventRegistrationTag {
-		acc, ok := msg.Item.(devremote.Account)
-		if !ok {
-			return fmt.Errorf("not a valid tx")
-		}
-		logg.InfoCtxf(ctx, "account emit", "account", acc)
-		ev := acc.ToRegistrationEvent()
-		err = d.h.Handle(ctx, apievent.EventRegistrationTag, &ev)
-	}
-	return err
-}
-
 func main() {
 	config.LoadConfig()
 
-	var accountService remote.AccountService
-	var fakeDir string
 	var connStr string
 	var size uint
 	var sessionId string
@@ -71,7 +39,6 @@ func main() {
 	flag.StringVar(&resourceDir, "resourcedir", scriptDir, "resource dir")
 	flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
 	flag.StringVar(&connStr, "c", "", "connection string")
-	flag.StringVar(&fakeDir, "fakedir", "", "if valid path, enables fake api with fsdb backend")
 	flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
 	flag.UintVar(&size, "s", 160, "max size of output")
 	flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory")
@@ -155,20 +122,7 @@ func main() {
 		os.Exit(1)
 	}
 
-	if fakeDir != "" {
-		svc := devremote.NewDevAccountService()
-		svc = svc.WithFs(ctx, fakeDir)
-		svc = svc.WithAutoVoucher(ctx, "FOO", 42)
-		eu := event.NewEventsUpdater(svc, menuStorageService)
-		emitter := &devEmitter{
-			h: eu.ToEventsHandler(),
-		}
-		svc = svc.WithEmitter(emitter.emit)
-		svc.AddVoucher(ctx, "BAR")
-		accountService = svc
-	} else {
-		accountService = &httpremote.HTTPAccountService{}
-	}
+	accountService := services.New(ctx, menuStorageService, connData)
 	hl, err := lhs.GetHandler(accountService)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "get accounts service handler: %v\n", err)
diff --git a/go.mod b/go.mod
index c00160d..9148b5f 100644
--- a/go.mod
+++ b/go.mod
@@ -3,10 +3,10 @@ module git.grassecon.net/grassrootseconomics/sarafu-vise
 go 1.23.4
 
 require (
-	git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d
+	git.defalsify.org/vise.git v0.2.3-0.20250115000535-e2d329b3f739
 	git.grassecon.net/grassrootseconomics/common v0.0.0-20250113174703-6afccefd1f05
-	git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250114142051-9645a2af7bef
-	git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250113213325-5228aef0889b
+	git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250115072214-bca7c5de969f
+	git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250115003256-c0534ede1b63
 	git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250113103030-f0b2056fd87d
 	github.com/alecthomas/assert/v2 v2.2.2
 	github.com/gofrs/uuid v4.4.0+incompatible
diff --git a/go.sum b/go.sum
index b4bb1f9..c450543 100644
--- a/go.sum
+++ b/go.sum
@@ -1,11 +1,11 @@
-git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d h1:bPAOVZOX4frSGhfOdcj7kc555f8dc9DmMd2YAyC2AMw=
-git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
+git.defalsify.org/vise.git v0.2.3-0.20250115000535-e2d329b3f739 h1:w7pj1oh7jXrfajahVYU7m7AfHst4C6jNVzDVoaqJ7e8=
+git.defalsify.org/vise.git v0.2.3-0.20250115000535-e2d329b3f739/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
 git.grassecon.net/grassrootseconomics/common v0.0.0-20250113174703-6afccefd1f05 h1:BenzGx6aDHKDwE23/mWIFD2InYIXyzHroZWV3MF5WUk=
 git.grassecon.net/grassrootseconomics/common v0.0.0-20250113174703-6afccefd1f05/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60=
-git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250114142051-9645a2af7bef h1:ukxNTuI+MF6ic2SkRlc8ZjlnsTR8g6owuHkAvnQNY8A=
-git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250114142051-9645a2af7bef/go.mod h1:BRIbXv0aulFWqR2XV87haWB4oNRlVEu8KyldLQsAroQ=
-git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250113213325-5228aef0889b h1:6SieNUSEKbkjzquuwazs/lVG56zdEWF10zQQEMRJfMs=
-git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250113213325-5228aef0889b/go.mod h1:E6W7ZOa7ZvVr0Bc5ot0LNSwpSPYq4hXlAIvEPy3AJ7U=
+git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250115072214-bca7c5de969f h1:FgccQi8vipX6dUt+GRiRDYHMR3UqC+plz73vw7y3fyU=
+git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250115072214-bca7c5de969f/go.mod h1:tbA4whUGMUIDgQVdIW0sxWPuuXOvZRSny5zeku5hX4k=
+git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250115003256-c0534ede1b63 h1:bX7klKZpX+ZZu1LKbtOXDAhV/KK0YwExehiIi0jusAM=
+git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250115003256-c0534ede1b63/go.mod h1:Syw9TZyigPAM7t9FvicOm36iUnidt45f0SxzT2JniQ8=
 git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250113103030-f0b2056fd87d h1:q/NO1rEgK3pia32D/tCq5hXfEuJp84COZRwceFvy/MM=
 git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250113103030-f0b2056fd87d/go.mod h1:AH15xABcvaJr1TCGlih3oGSuwWC0E5IdbHQwuu+E1KI=
 github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
diff --git a/services/local.go b/services/local.go
new file mode 100644
index 0000000..e79c8fb
--- /dev/null
+++ b/services/local.go
@@ -0,0 +1,50 @@
+// +build !online
+
+package services
+
+import (
+	"fmt"
+	"context"
+
+	"git.grassecon.net/grassrootseconomics/visedriver/storage"
+	devremote "git.grassecon.net/grassrootseconomics/sarafu-api/dev"
+	"git.grassecon.net/grassrootseconomics/sarafu-api/remote"
+	apievent "git.grassecon.net/grassrootseconomics/sarafu-api/event"
+	"git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/event"
+)
+
+type localEmitter struct {
+	h *apievent.EventsHandler
+}
+
+func (d *localEmitter) emit(ctx context.Context, msg apievent.Msg) error {
+	var err error
+	if msg.Typ == apievent.EventTokenTransferTag {
+		tx, ok := msg.Item.(devremote.Tx)
+		if !ok {
+			return fmt.Errorf("not a valid tx")
+		}
+		ev := tx.ToTransferEvent()
+		err = d.h.Handle(ctx, apievent.EventTokenTransferTag, &ev)
+	} else if msg.Typ == apievent.EventRegistrationTag {
+		acc, ok := msg.Item.(devremote.Account)
+		if !ok {
+			return fmt.Errorf("not a valid tx")
+		}
+		ev := acc.ToRegistrationEvent()
+		err = d.h.Handle(ctx, apievent.EventRegistrationTag, &ev)
+	}
+	return err
+}
+
+func New(ctx context.Context, storageService storage.StorageService, conn storage.ConnData) remote.AccountService {
+	svc := devremote.NewDevAccountService(ctx, storageService)
+	svc = svc.WithAutoVoucher(ctx, "FOO", 42)
+	eu := event.NewEventsUpdater(svc, storageService)
+	emitter := &localEmitter{
+		h: eu.ToEventsHandler(),
+	}
+	svc = svc.WithEmitter(emitter.emit)
+	svc.AddVoucher(ctx, "BAR")
+	return svc
+}
diff --git a/services/remote.go b/services/remote.go
new file mode 100644
index 0000000..2d31217
--- /dev/null
+++ b/services/remote.go
@@ -0,0 +1,15 @@
+// +build online
+
+package services
+
+import (
+	"context"
+
+	"git.grassecon.net/grassrootseconomics/visedriver/storage"
+	"git.grassecon.net/grassrootseconomics/sarafu-api/remote"
+	httpremote "git.grassecon.net/grassrootseconomics/sarafu-api/remote/http"
+)
+
+func New(ctx context.Context, storageService storage.StorageService, conn storage.ConnData) remote.AccountService {
+	return &httpremote.HTTPAccountService{}
+}