diff --git a/server_apps.go b/server_apps.go
index 7f374c9ac6f51341990c67963627f09799351375..81b1325a243f9a7cd5287db4d0f36aaca9da1dd7 100644
--- a/server_apps.go
+++ b/server_apps.go
@@ -18,6 +18,7 @@ import (
 	"strings"
 	"strconv"
 	"time"
+    "math"
 
 	"github.com/go-git/go-git/v5"
 	"github.com/go-git/go-git/v5/config"
@@ -27,6 +28,18 @@ import (
 	"github.com/pelletier/go-toml/v2"
 )
 
+type appPatch struct{
+    repository string
+	zip       []byte
+	targz     []byte
+}
+
+type appStatus struct{
+	tested_version int
+	broken  bool
+    patch *appPatch
+}
+
 type appRegistry struct {
 	shame []*appShame
 	apps  []*appDescriptor
@@ -58,6 +71,8 @@ type appInfo struct {
 	targz     []byte
 
 	firstErr error
+    tags    []string
+    status *appStatus
 }
 
 type GLProject struct {
@@ -95,7 +110,7 @@ func (s *server) getStars(ctx context.Context, repo string) (int, error) {
 	return project.StarCount, nil
 }
 
-func (s *server) zipApp(ctx context.Context, name, pathInRepo, repo string, obj *object.Commit) ([]byte, error) {
+func (s *server) zipApp(ctx context.Context, name, pathInRepo, repo string, patch_source *string, obj *object.Commit) ([]byte, error) {
 	fi, err := obj.Files()
 	if err != nil {
 		return nil, fmt.Errorf("listing files: %w", err)
@@ -134,6 +149,9 @@ func (s *server) zipApp(ctx context.Context, name, pathInRepo, repo string, obj
 		if err != nil {
 			return nil, fmt.Errorf("Blob.Reader: %w", err)
 		}
+        if f.Name == "flow3r.toml" && patch_source != nil{
+            fo.Write([]byte(fmt.Sprintf("patch_source = \"%s\"\n\n", *patch_source)))
+        }
 		_, err = io.Copy(fo, rdr)
 		rdr.Close()
 		if err != nil {
@@ -146,7 +164,7 @@ func (s *server) zipApp(ctx context.Context, name, pathInRepo, repo string, obj
 	return buf.Bytes(), nil
 }
 
-func (s *server) targzApp(ctx context.Context, name, pathInRepo, repo string, obj *object.Commit) ([]byte, error) {
+func (s *server) targzApp(ctx context.Context, name, pathInRepo, repo string, patch_source * string, obj *object.Commit) ([]byte, error) {
 	fi, err := obj.Files()
 	if err != nil {
 		return nil, fmt.Errorf("listing files: %w", err)
@@ -210,10 +228,14 @@ func (s *server) targzApp(ctx context.Context, name, pathInRepo, repo string, ob
 				}
 			}
 		}
+        patch_bytes := []byte("")
+        if f.Name == "flow3r.toml" && patch_source != nil{
+            patch_bytes = []byte(fmt.Sprintf("patch_source = \"%s\"\n\n", *patch_source))
+        }
 		err = t.WriteHeader(&tar.Header{
 			Name:     outPath,
 			Typeflag: tar.TypeReg,
-			Size:     f.Size,
+			Size:     f.Size + int64(len(patch_bytes)),
 			Mode:     0644,
 		})
 		if err != nil {
@@ -226,6 +248,12 @@ func (s *server) targzApp(ctx context.Context, name, pathInRepo, repo string, ob
 		if err != nil {
 			return nil, fmt.Errorf("Blob.Reader: %w", err)
 		}
+        if len(patch_bytes) != 0{
+            t.Write(patch_bytes)
+        }
+		if err != nil {
+			return nil, fmt.Errorf("patch_source: %w", err)
+		}
 		_, err = io.Copy(t, rdr)
 		rdr.Close()
 		if err != nil {
@@ -286,9 +314,10 @@ func (s *server) parseAppToml(ctx context.Context, pathInRepo string, obj *objec
 		"Music": true,
 		"Media": true,
 		"Games": true,
+		"Demos": true,
 	}
 	if !sections[data.App.Category] {
-		return nil, fmt.Errorf("app category invalid (must be one of 'Badge', 'Apps', 'Music', 'Media', 'Games')")
+		return nil, fmt.Errorf("app category invalid (must be one of 'Badge', 'Apps', 'Music', 'Media', 'Games', 'Demos')")
 	}
 	if data.Entry.Class == "" {
 		return nil, fmt.Errorf("no entry class")
@@ -311,7 +340,7 @@ func (s *server) parseAppToml(ctx context.Context, pathInRepo string, obj *objec
 	}, nil
 }
 
-func (s *server) getAppInfo(ctx context.Context, pathInRepo, repo string) (*appInfo, error) {
+func (s *server) getAppInfo(ctx context.Context, pathInRepo, repo string, slug *string) (*appInfo, error) {
 	url := "https://git.flow3r.garden/" + repo
 	g, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{
 		URL: url,
@@ -389,14 +418,21 @@ func (s *server) getAppInfo(ctx context.Context, pathInRepo, repo string) (*appI
 	if err != nil {
 		return nil, fmt.Errorf("getting gitlab stars failed: %w", err)
 	}
+    var patch_source *string = nil
+    slug_str := repo
+    if slug != nil{
+        slug_str = *slug
+        psrc := fmt.Sprintf("https://git.flow3r.garden/%s", repo)
+        patch_source = &psrc
+    }
 	app := firstTime[highestVer]
 	app.stars = stars
-	zbytes, err := s.zipApp(ctx, app.name, pathInRepo, repo, app.commitObj)
+	zbytes, err := s.zipApp(ctx, app.name, pathInRepo, slug_str, patch_source, app.commitObj)
 	if err != nil {
 		return nil, fmt.Errorf("zipping failed: %w", err)
 	}
 	app.zip = zbytes
-	tbytes, err := s.targzApp(ctx, app.name, pathInRepo, repo, app.commitObj)
+	tbytes, err := s.targzApp(ctx, app.name, pathInRepo, slug_str, patch_source, app.commitObj)
 	if err != nil {
 		return nil, fmt.Errorf("targzing failed: %w", err)
 	}
@@ -413,6 +449,7 @@ func (s *server) getAppInfo(ctx context.Context, pathInRepo, repo string) (*appI
 
 	app.firstErr = firstErr
 
+    app.tags = make([]string, 0)
 	return app, nil
 }
 
@@ -446,6 +483,26 @@ func (s *server) getAppRegistry(ctx context.Context) (*appRegistry, error) {
 	if err != nil {
 		return nil, err
 	}
+    flags, err := obj.File("tags.json")
+	if err != nil {
+		return nil, err
+	}
+    flagreader, err := flags.Reader()
+	if err != nil {
+		return nil, err
+	}
+    flagbytes, err := io.ReadAll(flagreader)
+	if err != nil {
+		return nil, err
+	}
+
+    var flagmap map[string]interface{}
+    
+    err = json.Unmarshal([]byte(flagbytes), &flagmap)
+	if err != nil {
+		return nil, err
+	}
+
 	iter, err := obj.Files()
 	if err != nil {
 		return nil, err
@@ -508,7 +565,7 @@ func (s *server) getAppRegistry(ctx context.Context) (*appRegistry, error) {
 	}
 
 	for _, app := range registry.apps {
-		info, err := s.getAppInfo(ctx, app.pathInRepo, app.repository)
+		info, err := s.getAppInfo(ctx, app.pathInRepo, app.repository, nil)
 		if err != nil {
 			log.Printf("App %q: %v", app.repository, err)
 			registry.shame = append(registry.shame, &appShame{
@@ -522,9 +579,44 @@ func (s *server) getAppRegistry(ctx context.Context) (*appRegistry, error) {
 				repository: app.repository,
 				errorMsg:   fmt.Sprintf("%v", info.firstErr),
 			})
+		} else if app_flags, ok := flagmap[app.repository].(map[string]interface{}); ok {
+            info.status = new(appStatus)
+            printed := false
+            if tags, ok := app_flags["tags"].([]interface{}); ok {
+                for _, tag := range tags{
+                    if tagtxt, ok := tag.(string); ok {
+                        info.tags = append(info.tags, tagtxt)
+                    }
+                }
+            }
+            if status, ok := app_flags["status"].(map[string]interface{}); ok {
+                if tested, ok := status["tested_version"].(float64); ok {
+                    info.status.tested_version = int(math.Round(tested))
+                }
+                if broken, ok := status["broken"].(bool); ok {
+                    info.status.broken = broken
+                }
+                if patch, ok := status["patch"].(string); ok {
+                    printed = true
+                    patch_info, err := s.getAppInfo(ctx, "", patch, &app.repository)
+                    if err != nil {
+                        log.Printf("App %q: couldn't get patch at %q: %v", app.repository, patch, err)
+                    } else {
+                        log.Printf("App: %q: okay, patch found", app.repository)
+                        info.status.patch = &appPatch{
+                            repository: patch,
+                            zip: patch_info.zip,
+                            targz: patch_info.targz,
+                        }
+                    }
+                }
+            }
+            if !printed{
+			    log.Printf("App: %q: okay", app.repository)
+            }
 		} else {
 			log.Printf("App: %q: okay", app.repository)
-		}
+        }
 		app.appInfo = info
 	}
 
@@ -533,6 +625,18 @@ func (s *server) getAppRegistry(ctx context.Context) (*appRegistry, error) {
 	return &registry, nil
 }
 
+type jsonAppPatch struct{
+    RepoURL        string    `json:"repoUrl"`
+	DownloadURL    string    `json:"downloadUrl"`
+	TarDownloadURL string    `json:"tarDownloadUrl"`
+}
+
+type jsonAppStatus struct{
+	TestedVersion  int          `json:"tested_version"`
+	Broken         bool         `json:"broken"`
+    Patch          jsonAppPatch `json:"patch"`
+}
+
 type jsonApp struct {
 	RepoURL        string    `json:"repoUrl"`
 	Commit         string    `json:"commit"`
@@ -543,13 +647,15 @@ type jsonApp struct {
 	Author         string    `json:"author"`
 	Description    string    `json:"description"`
 	Version        int       `json:"version"`
-	Timestamp      time.Time `json:"timestamp"`
 	Stars          int       `json:"stars"`
+    Tags           []string  `json:"tags"`
+	Timestamp      time.Time `json:"timestamp"`
 	Flow3rSeed     string    `json:"flow3rSeed"`
+    Status          *jsonAppStatus  `json:"status"`
 }
 
 func makeJsonApp(a *appDescriptor) jsonApp {
-	return jsonApp{
+	ret := jsonApp{
 		RepoURL:        "https://git.flow3r.garden/" + a.repository,
 		Commit:         a.appInfo.commit,
 		DownloadURL:    fmt.Sprintf("%sapps/zip/%s.zip", flagBaseURL, a.repository),
@@ -562,7 +668,23 @@ func makeJsonApp(a *appDescriptor) jsonApp {
 		Timestamp:      a.appInfo.commitObj.Committer.When.UTC(),
 		Stars:          a.appInfo.stars,
 		Flow3rSeed:     a.appInfo.flow3rSeed,
-	}
+        Tags:           a.appInfo.tags,
+	}
+    if a.appInfo.status != nil{
+        ret.Status = &jsonAppStatus{
+            Broken:             a.appInfo.status.broken,
+            TestedVersion:      a.appInfo.status.tested_version,
+        }
+        if a.appInfo.status.patch != nil{
+            pa := a.appInfo.status.patch.repository
+            ret.Status.Patch = jsonAppPatch{
+		        RepoURL:        "https://git.flow3r.garden/" + pa,
+                DownloadURL:    fmt.Sprintf("%sapps/zip/%s.zip", flagBaseURL, pa),
+                TarDownloadURL: fmt.Sprintf("%sapps/tar/%s.tar.gz", flagBaseURL, pa),
+            }
+        }
+    }
+    return ret
 }
 
 func (s *server) handleApps(w http.ResponseWriter, r *http.Request) {
@@ -682,12 +804,22 @@ func (s *server) handleAppZip(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+    repository := fmt.Sprintf("%s/%s", orga, repo)
+
 	for _, app := range apps {
-		if app.repository == fmt.Sprintf("%s/%s", orga, repo) {
+		if app.repository == repository {
 			w.Header().Add("Content-Type", "application/zip")
 			w.Write(app.appInfo.zip)
 			return
 		}
+        if app.appInfo == nil || app.appInfo.status == nil || app.appInfo.status.patch == nil {
+            continue
+        }
+		if app.appInfo.status.patch.repository == repository {
+			w.Header().Add("Content-Type", "application/zip")
+			w.Write(app.appInfo.status.patch.zip)
+			return
+		}
 	}
 
 	http.NotFound(w, r)
@@ -703,6 +835,7 @@ func (s *server) handleAppTargz(w http.ResponseWriter, r *http.Request) {
 	}
 	orga := matches[1]
 	repo := matches[2]
+    repository := fmt.Sprintf("%s/%s", orga, repo)
 
 	apps, err := s.getApps(ctx)
 	if err != nil {
@@ -710,12 +843,21 @@ func (s *server) handleAppTargz(w http.ResponseWriter, r *http.Request) {
 	}
 
 	for _, app := range apps {
-		if app.repository == fmt.Sprintf("%s/%s", orga, repo) {
+		if app.repository == repository {
 			w.Header().Add("Content-Type", "application/x-gzip")
 			w.Header().Add("Content-Length", strconv.Itoa(len(app.appInfo.targz)))
 			w.Write(app.appInfo.targz)
 			return
 		}
+        if app.appInfo == nil || app.appInfo.status == nil || app.appInfo.status.patch == nil {
+            continue
+        }
+		if app.appInfo.status.patch.repository == repository {
+			w.Header().Add("Content-Type", "application/x-gzip")
+			w.Header().Add("Content-Length", strconv.Itoa(len(app.appInfo.status.patch.targz)))
+			w.Write(app.appInfo.status.patch.targz)
+			return
+		}
 	}
 
 	http.NotFound(w, r)