Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ go 1.25.0
require golang.org/x/crypto v0.51.0

require golang.org/x/sys v0.44.0 // indirect

replace github.com/pilot-protocol/common => ../common
4 changes: 2 additions & 2 deletions integration/adapter.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Package integration is the glue layer between the app-store shim and
// the pilot daemon. It imports both modules and provides an Adapter that
// satisfies github.com/TeoSlayer/pilotprotocol/pkg/coreapi.Service by
// satisfies github.com/pilot-protocol/common/coreapi.Service by
// forwarding to *appstore.Service.
//
// This package is intentionally *not* part of the main app-store module's
Expand All @@ -13,7 +13,7 @@ package integration
import (
"context"

"github.com/TeoSlayer/pilotprotocol/pkg/coreapi"
"github.com/pilot-protocol/common/coreapi"
"github.com/pilot-protocol/app-store/plugin/appstore"
)

Expand Down
2 changes: 1 addition & 1 deletion integration/adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"testing"
"time"

"github.com/TeoSlayer/pilotprotocol/pkg/coreapi"
"github.com/pilot-protocol/common/coreapi"
"github.com/pilot-protocol/app-store/plugin/appstore"
)

Expand Down
2 changes: 1 addition & 1 deletion integration/broker_spawn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"testing"
"time"

"github.com/TeoSlayer/pilotprotocol/pkg/coreapi"
"github.com/pilot-protocol/common/coreapi"
"github.com/pilot-protocol/app-store/plugin/appstore"
)

Expand Down
10 changes: 4 additions & 6 deletions integration/go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
module github.com/pilot-protocol/app-store/integration

go 1.25.3
go 1.25.10

require (
github.com/TeoSlayer/pilotprotocol v0.0.0
github.com/pilot-protocol/app-store v0.0.0
github.com/pilot-protocol/app-store v0.1.0
github.com/pilot-protocol/common v0.2.0
)

replace github.com/pilot-protocol/app-store => ..

replace github.com/TeoSlayer/pilotprotocol => /Users/calinteodor/Development/web4
replace github.com/pilot-protocol/common => ../../common
2 changes: 2 additions & 0 deletions integration/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/pilot-protocol/app-store v0.1.0 h1:mMEbr04GURXWuFd4kQBONZZK+AMrXxdVt+IujeySfo8=
github.com/pilot-protocol/app-store v0.1.0/go.mod h1:0fo1XjzzLHmRMGuTc22aOLAseQzms7qM4QXfGilmMWY=
4 changes: 2 additions & 2 deletions integration/spawn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ import (
"testing"
"time"

"github.com/TeoSlayer/pilotprotocol/pkg/coreapi"
"github.com/pilot-protocol/common/coreapi"
"github.com/pilot-protocol/app-store/pkg/ipc"
"github.com/pilot-protocol/app-store/plugin/appstore"
)

// walletSourceDir is the path to the wallet module relative to the dev
// layout. If absent (e.g. CI running on a checkout without sibling apps),
// spawn-tests t.Skip rather than fail.
const walletSourceDir = "/Users/calinteodor/Development/web4-apps/wallet"
const walletSourceDir = "/Users/calinteodor/Development/pilot-protocol/wallet"

// TestSupervisorSpawnsAndServesWallet builds the wallet binary, pins it
// into a fake install root with a real manifest, starts the supervisor
Expand Down
6 changes: 6 additions & 0 deletions plugin/appstore/supervisor.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,12 @@ func (s *supervisor) rescanForNew() []*installedApp {
delete(s.ready, a.Manifest.ID)
s.logger.Printf("rescan: version upgrade detected: app=%s %s → %s — restarting",
a.Manifest.ID, existing.Manifest.AppVersion, a.Manifest.AppVersion)
s.writeAuditLine(a, auditEvent{
Event: "upgrade-applied",
Reason: fmt.Sprintf("rescan: %s → %s", existing.Manifest.AppVersion, a.Manifest.AppVersion),
SHA256: a.Manifest.Binary.SHA256,
BinaryAt: a.BinaryPath,
})
}
s.installed[a.Manifest.ID] = a
fresh = append(fresh, a)
Expand Down
81 changes: 76 additions & 5 deletions plugin/appstore/zz4_downgrade_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package appstore

import (
"crypto/ed25519"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"testing"

"github.com/pilot-protocol/app-store/pkg/manifest"
)

// TestCompareVersions exercises the semver comparison helper.
Expand Down Expand Up @@ -135,7 +142,9 @@ func TestRegisterSameVersionIsIdempotent(t *testing.T) {
}

// writeAppDirWithVersion creates a valid app dir with a manifest
// carrying the given version, and a stub binary.
// carrying the given version, and a stub binary. The manifest is
// signed with a fresh ed25519 keypair so scanInstalled's signature
// verification (PILOT-98) accepts it.
func writeAppDirWithVersion(t *testing.T, root, id, version string) string {
t.Helper()
dir := filepath.Join(root, id)
Expand All @@ -149,11 +158,33 @@ func writeAppDirWithVersion(t *testing.T, root, id, version string) string {
if err := os.WriteFile(filepath.Join(binDir, "x"), []byte("#!/bin/sh\necho ok"), 0o755); err != nil {
t.Fatal(err)
}
raw := fmt.Sprintf(
`{"id":%q,"app_version":%q,"manifest_version":1,"binary":{"runtime":"go","path":"bin/x","sha256":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"},"grants":[{"cap":"net.dial","target":"*"}],"store":{"publisher":"ed25519:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","signature":"sig"}}`,
id, version,

pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("generate key: %v", err)
}
pubB64 := base64.StdEncoding.EncodeToString(pub)

template := fmt.Sprintf(
`{"id":%q,"app_version":%q,"manifest_version":1,"binary":{"runtime":"go","path":"bin/x","sha256":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"},"grants":[{"cap":"net.dial","target":"*"}],"store":{"publisher":"ed25519:%s","signature":""}}`,
id, version, pubB64,
)
if err := os.WriteFile(filepath.Join(dir, "manifest.json"), []byte(raw), 0o644); err != nil {
m, err := manifest.Parse([]byte(template))
if err != nil {
t.Fatalf("parse template: %v", err)
}
grantsJSON, _ := json.Marshal(m.Grants)
grantsHash := sha256.Sum256(grantsJSON)
payload := fmt.Sprintf("%s:%s:%d:%s:%x",
m.Store.Publisher, m.ID, m.ManifestVersion, m.Binary.SHA256, grantsHash)
sig := ed25519.Sign(priv, []byte(payload))
m.Store.Signature = base64.StdEncoding.EncodeToString(sig)

raw, err := json.Marshal(m)
if err != nil {
t.Fatalf("marshal signed manifest: %v", err)
}
if err := os.WriteFile(filepath.Join(dir, "manifest.json"), raw, 0o644); err != nil {
t.Fatal(err)
}
return dir
Expand Down Expand Up @@ -232,6 +263,46 @@ func TestRescanAllowsUpgradeMidRun(t *testing.T) {
}
}

// TestRescanAuditLogsUpgradeApplied confirms the supervisor writes an
// "upgrade-applied" audit event when an in-place version swap lands
// (symmetric counterpart to "downgrade-refused").
func TestRescanAuditLogsUpgradeApplied(t *testing.T) {
root := t.TempDir()
appDir := writeAppDirWithVersion(t, root, "io.test.app", "1.0.0")

sup := newSupervisor(Config{
InstallRoot: root,
RescanInterval: 20 * 1e6,
}, Deps{}, newQuietLogger(t))

entry := &installedApp{
Dir: appDir,
Manifest: parseDummyManifest(t, "io.test.app"),
BinaryPath: filepath.Join(appDir, "bin/x"),
}
entry.Manifest.AppVersion = "1.0.0"
sup.mu.Lock()
sup.installed["io.test.app"] = entry
sup.mu.Unlock()

writeAppDirWithVersion(t, root, "io.test.app", "2.0.0")
if fresh := sup.rescanForNew(); len(fresh) != 1 {
t.Fatalf("upgrade should be accepted, got fresh=%d", len(fresh))
}

data, err := os.ReadFile(filepath.Join(appDir, supervisorLogName))
if err != nil {
t.Fatalf("read audit log: %v", err)
}
body := string(data)
if !strings.Contains(body, "upgrade-applied") {
t.Errorf("audit log missing upgrade-applied event:\n%s", body)
}
if !strings.Contains(body, "1.0.0") || !strings.Contains(body, "2.0.0") {
t.Errorf("audit log missing version transition 1.0.0 → 2.0.0:\n%s", body)
}
}

// TestRescanAuditLogsDowngradeRefusal confirms the supervisor writes an
// audit event when refusing a downgrade during rescan.
func TestRescanAuditLogsDowngradeRefusal(t *testing.T) {
Expand Down
Loading